这并不是一个有建设性的回答,只是一些想法。首先,免责声明 - 我不建议以下任何方法作为良好的实践(可能通常它们不是),它们只是一些似乎可以解决您特定问题的可能性。关于所述目标 - 我非常支持这个想法,能够减少冗余很棒(至少对于独立开发者的个人需求)。至于工具:我对符号包的经验非常少,但无论是否使用它或编写一些自定义的框操作预处理器,我的感觉是整个事实,即输入表达式必须由Mathematica解析器解析为框严重限制了可以做的事情。此外,将在包中使用它可能会遇到困难,正如其他回复中已经提到的。
如果有像$PreRead
这样的钩子,最简单的方法就是允许用户拦截输入字符串并将其处理成另一个字符串,然后再将其馈送给解析器。那将允许编写一个自定义的预处理器,它在字符串级别上运行 - 或者您可以称之为编译器,从您设计的任何语法字符串生成Mathematica代码。我不知道这样的挂钩(当然可能是我的无知)。如果缺少这个,可以使用例如program样式的单元格,并且可能编写一些按钮,从这些单元格中读取字符串,并调用这样的预处理器来生成Mathematica代码并将其粘贴到原始代码所在的单元格旁边。
如果您想要的语言是一种简单的语言(至少在其语法和语法上),那么这种预处理器方法将最有效,以便易于词法分析和解析。如果您想要Mathematica语言(除了您想要更改的几个元素之外的全部语法),在这种方法中,您将没有运气,因为无论您的更改有多少和“轻量级”,您都需要重新实现基本上完全的Mathematica解析器,只是为了使这些更改可靠地工作。换句话说,我想说的是,在我看来,编写一个预处理器,它将从类似Lisp的语言生成Mathematica代码,语法很少或没有,比尝试对标准mma进行一些语法修改要容易得多。
从技术上讲,编写这样的预处理器的一种方法是使用像Lex(Flex)和Yacc(Bison)之类的标准工具来定义您的语法并生成解析器(例如在C中)。这样的解析器可以通过MathLink或LibraryLink(在C的情况下)插入回Mathematica。它的最终结果将是一个字符串,当解析时,它将成为有效的Mathematica表达式。该表达式将表示您解析的代码的抽象语法树。例如,像这样的代码(此处介绍了Fold
的新语法)
"((1|+|{2,3,4,5}))"
可以解释为类似于
"functionCall[fold,{plus,1,{2,3,4,5}}]"
这样的预处理器的第二个组件将使用Mathematica编写,可能以基于规则的方式,从AST生成Mathematica代码。生成的代码必须以某种方式保持未评估状态。对于上面的代码,结果可能如下:
Hold[Fold[Plus,1,{2,3,4,5}]]
最好能够在Mathematica中使用类似Lex(Flex)/Yacc(Bison)这样的工具,即绑定(Bindings),只需在Mathematica中编写代码,就可以自动生成C解析器,并通过MathLink或LibraryLink插入到内核中。我希望它们将在未来版本中提供。如果没有这个工具,所述方法需要大量的低级工作(C或Java)。然而,我认为它仍然可行。如果您会写C(或Java),可以尝试编写一些相对简单(从语法/语法角度来看)的语言 - 这可能是一个有趣的项目,并且可以了解更复杂语言的情况。我会从一个非常基本的计算器示例开始,然后将标准算术运算符更改为一些Mathematica无法正确解析的更奇怪的运算符,以使其更有趣。为了避免MathLink/LibraryLink的复杂性并进行测试,您可以使用
Run
从Mathematica调用生成的可执行文件,并将代码作为命令行参数之一传递,然后将结果写入临时文件,再将其导入到Mathematica中。对于计算器示例,整个过程可以在几小时内完成。
当然,如果您只想缩写某些长函数名称,有一个更简单的替代方案 - 您可以使用
With
。以下是一个实际示例,即我移植的Peter Norvig的
拼写校正器,在其中我使用了这种方法来减少行数:
Clear[makeCorrector];
makeCorrector[corrector_Symbol, trainingText_String] :=
Module[{model, listOr, keys, words, edits1, train, max, known, knownEdits2},
(* Proxies for some commands - just to play with syntax a bit*)
With[{fn = Function, join = StringJoin, lower = ToLowerCase,
rev = Reverse, smatches = StringCases, seq = Sequence, chars = Characters,
inter = Intersection, dv = DownValues, len = Length, ins = Insert,
flat = Flatten, clr = Clear, rep = ReplacePart, hp = HoldPattern},
(* body *)
listOr = fn[Null, Scan[If[# =!= {}, Return[#]] &, Hold[##]], HoldAll];
keys[hash_] := keys[hash] = Union[Most[dv[hash][[All, 1, 1, 1]]]];
words[text_] := lower[smatches[text, LetterCharacter ..]];
With[{m = model},
train[feats_] := (clr[m]; m[_] = 1; m[#]++ & /@ feats; m)];
With[{nwords = train[words[trainingText]],
alphabet = CharacterRange["a", "z"]},
edits1[word_] := With[{c = chars[word]}, join @@@ Join[
Table[
rep[c, c, #, rev[#]] &@{{i}, {i + 1}}, {i, len[c] - 1}],
Table[Delete[c, i], {i, len[c]}],
flat[Outer[#1[c, ##2] &, {ins[#1, #2, #3 + 1] &, rep},
alphabet, Range[len[c]], 1], 2]]];
max[set_] := Sort[Map[{nwords[#], #} &, set]][[-1, -1]];
known[words_] := inter[words, keys[nwords]]];
knownEdits2[word_] := known[flat[Nest[Map[edits1, #, {-1}] &, word, 2]]];
corrector[word_] := max[listOr[known[{word}], known[edits1[word]],
knownEdits2[word], {word}]];]];
您需要一些训练文本作为字符串传递给函数的第二个参数,而第一个参数是纠错器的函数名。这是Norvig使用的一个例子:
text = Import["http://norvig.com/big.txt", "Text"]
你只需调用一次,例如:
In[7]:= makeCorrector[correct, text]
然后在一些单词上使用它任意次数
In[8]:= correct["coputer"]
Out[8]= {0.125, "computer"}
你可以创建自定义的类似于
With
的控制结构,其中硬编码一些最让你感到烦恼的长名称的简称,并将其包装在你的代码片段周围(但是您将失去代码高亮)。请注意,我通常不提倡此方法 - 我只是为了好玩和减少代码行数而这样做。但至少,它在交互式和包中都能够工作。无法使用中缀运算符,无法更改优先级等等,但几乎没有任何工作量。
CellEvaluationFunction
钩子可以用于这种处理。请参见我在此主题上的一个答案。 - WReach.mt
包,但其中一个问题可能是包导入 - 显式或隐藏对Needs
的调用。我还有一种感觉,使用FrontEnd处理包作为一种通用方法在概念上是错误的。 - Leonid Shifrin