【问题标题】:Mathematica Notation and syntax modsMathematica 符号和语法模型
【发布时间】:2011-03-26 01:00:53
【问题描述】:

我正在使用 Notation 包在 Mathematica 中尝试语法模块。

我对特定领域的数学符号不感兴趣,但对通用语法修改和扩展感兴趣,尤其是减少 Mathematica 的 VeryLongFunctionNames 冗长、清理笨拙的结构或以令人愉悦的方式扩展语言的符号。

一个示例修改是将Fold[f, x] 定义为Fold[f, First@x, Rest@x]
这很好用,也很方便。

另一个将定义*{1,2} 评估为Sequence @@ {1,2} 受Python 的启发;这在 Mathematica 中可能有效,也可能无效。

请提供信息或链接地址:

  • 符号和语法修改的限制

  • 实施技巧和窍门

  • 现有包、示例或实验

  • 为什么这是一个好主意或坏主意

【问题讨论】:

    标签: syntax wolfram-mathematica notation


    【解决方案1】:

    不是一个真正有建设性的答案,只是一些想法。首先,免责声明 - 我不建议将下面描述的任何方法作为良好实践(也许通常它们不是),它们只是似乎解决您的特定问题的一些可能性。关于既定目标 - 我非常支持这个想法,能够减少冗长是很棒的(至少对于独立开发人员的个人需求)。至于工具:我对 Notation 包的经验很少,但是,无论是否使用它或编写一些自定义的框操作预处理器,我的感觉是输入表达式必须由 Mathematica 解析器解析成框的整个事实严重限制了一些可以做的事情。此外,正如在另一个回复中已经提到的那样,在包中使用它可能会遇到困难。

    如果有一些像$PreRead 这样的钩子,那将是最简单的,它允许用户截取输入字符串并将其处理为另一个字符串,然后再将其提供给解析器。这将允许人们编写一个在字符串级别运行的自定义预处理器 - 或者如果您愿意,您可以将其称为编译器 - 这将采用您设计的任何语法的字符串并从中生成 Mathematica 代码。我不知道这样的钩子(当然这可能是我的无知)。缺少这一点,可以使用例如 program 样式的单元格并可能编写一些按钮,这些按钮从这些单元格中读取字符串并调用此类预处理器以生成 Mathematica 代码并将其粘贴到旁边的单元格中原始代码在哪里。

    如果您想要的语言是一些简单的语言(至少就其语法和语法而言),这种预处理器方法将最有效,以便于词法分析和解析。如果您想要 Mathematica 语言(其完整的语法模数只是您想要更改的几个元素),在这种方法中,您不走运,因为无论您的更改有多少和“轻量级”,您d 需要几乎完全重新实现 Mathematica 解析器,只是为了进行这些更改,如果您希望它们可靠地工作。换句话说,我要说的是,IMO 编写一个预处理器要容易得多,它可以从一些类似 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) 之类的工具(我的意思是绑定,它只需要在 Mathematica 中编写代码,然后自动生成 C 解析器,通过 MathLink 或 LibraryLink 将其插入内核)。我可能只希望它们将在未来的某些版本中可用。缺少这一点,我描述的方法将需要大量的低级工作(如果您愿意,可以使用 C 或 Java)。不过我觉得还是可以的。如果您可以编写 C(或 Java),您可能会尝试编写一些相当简单(就语法/文法而言)的语言——这可能是一个有趣的项目,并且会让您了解更复杂的语言会是什么样子一。我将从一个非常基本的计算器示例开始,也许将那里的标准算术运算符更改为 Mathematica 本身无法正确解析的一些更奇怪的运算符,以使其更有趣。为了避免 MathLink/LibraryLink 的复杂性,您可以使用 Run 从 Mathematica 调用生成的可执行文件,将代码作为命令行参数之一传递,并将结果写入临时文件,然后将其导入进入数学。对于计算器示例,整个事情可以在几个小时内完成。

    当然,如果您只想缩写某些长函数名称,还有一个更简单的替代方法 - 您可以使用 With 来做到这一点。这是一个实际的例子 - 我的 Peter Norvig 的 spelling corrector 端口,我以这种方式作弊以减少行数:

    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"] // Timing
    
    Out[8]= {0.125, "computer"}
    

    您可以创建您的自定义With-like 控制结构,在其中硬编码一些最让您烦恼的长 mma 名称的短名称,然后将其包裹在您的代码段(您将丢失但是代码突出显示)。请注意,我通常不提倡这种方法 - 我这样做只是为了好玩并减少行数。但至少,从某种意义上说,这是通用的,它既可以交互地工作,也可以在包中工作。不能做中缀运算符,不能改变优先级等等,但几乎是零工作。

    【讨论】:

    • +1 在笔记本界面的上下文中,CellEvaluationFunction 钩子可用于此类处理。见one of my answers on this topic
    • 有人给了我一个关于Notation/Package 问题的讨论链接,但我放错了位置。当我在寻找它时,您知道是否有人尝试过将包作为字符串加载并使用 FrontEnd 处理的解决方法?
    • 这是一个您可能会感兴趣的相关问题的链接:groups.google.com/group/comp.soft-sys.math.mathematica/…。关于作为字符串加载 - 它可能有效,但由于包解析的工作方式也可能会出现并发症。对于代表 Mathematica 单元测试的 .mt 包,我能够使其用于我的特定目的。问题之一可能是包导入 - 对Needs 的显式或隐藏调用。我也有一种感觉,使用 FrontEnd 作为通用方法处理包在概念上是错误的。
    • @WReach 非常感谢您的链接,它会让我的生活更轻松。对我来说,这是一个缺失的部分。
    【解决方案2】:

    (我的第一个回复/帖子....要温柔)

    根据我的经验,该功能似乎有点像编程死胡同。定义自定义符号的能力似乎在很大程度上取决于使用“符号调色板”来定义和清除每个自定义符号。 ('一切都是一个表达式'......好吧,除了一些晦涩的情况,比如 Notations,你必须使用调色板。)太糟糕了。

    Notation 包文档明确提到了这一点,所以我不能抱怨太多。

    如果您只想在特定笔记本中定义自定义符号,Notations 可能对您有用。另一方面,如果您的目标是在 YourOwnPackage.m 中实现自定义符号并将它们分发给其他人,您可能会遇到问题。 (除非你非常精通 Box 结构?)

    如果有人能纠正我在这方面的无知,你会成为我的一个月! :)

    (我希望使用 Notations 来强制 MMA 将下标变量视为符号。)

    【讨论】:

    • 欢迎来到 StackOverflow。感谢您分享您的经历。您不是第一个建议 Notation 定义和包存在问题的人。到目前为止,我将我的修改限制在不需要Notation$PreRead 的东西上(例如两个参数Fold),但我想探索这些方法的功能和局限性。
    • @telefunkenvf14 嗨!我想我在数学组遇到过你几次,对吧?也欢迎来到这里。
    • @telefunkenvf14 哈!你的昵称让我想起了这里的旧时音频黑客。欢迎。
    • @telefunkenvf14 您可以强制 MMA 将下标变量视为符号:Needs["Notation``"]; Symbolize[ ParsedBoxWrapper[SubscriptBox["_", "_"]]]。您必须使用Notation 包,但不必使用调色板。 (这是Needs 中的一个反引号——无法弄清楚降价)。
    • @WReach:如果您需要在代码中使用反引号,请使用双反引号标记代码:即Needs["Notation`"]。 (当然,如果你需要一个反引号和一个双反引号怎么办?)
    【解决方案3】:

    不是一个完整的答案,只是为了展示我学到的一个技巧here(我认为与符号重新定义有关而不是符号):

    Unprotect[Fold];
    Fold[f_, x_] :=
      Block[{$inMsg = True, result},
        result = Fold[f, First@x, Rest@x];
        result] /; ! TrueQ[$inMsg];
    Protect[Fold];
    
    Fold[f, {a, b, c, d}]
    (*
    --> f[f[f[a, b], c], d]
    *)
    

    编辑

    感谢@rcollyer 提供以下内容(参见下面的 cmets)。

    您可以使用 $inMsg 变量随意打开或关闭定义:

    $inMsg = False;
    Fold[f, {a, b, c, d}]
    (*
    ->f[f[f[a,b],c],d]
    *)
    
    $inMsg = True;
    Fold[f, {a, b, c, d}]
    (*
    ->Fold::argrx: (Fold called with 2 arguments; 3 arguments are expected. 
    *)
    
    Fold[f, {a, b, c, d}]
    

    这在测试时非常宝贵

    【讨论】:

    • +1,这是Execute Around Block 的一种形式,还有其他名称的变体:RAIIExecute Around Pointer。我的OpenAndRead 方法对文件流做同样的事情。
    • @Simon 重命名似乎没有必要(除了出于可读性的原因),因为 $inMsg 永远不会在 Block 范围之外定义。
    • @rcollyer Ahhh 这就是为什么它使用 Block[ ] 而不是 Module[ ] ... 来引用同一个 var :D
    • @belisarius, @rcollyer 我还是会把Module[{inFunction}, ...] 包裹在所有东西上,比如Module[{inFunction},f[x_]:=Block[{inFunction = True}, codeBefore;f[x];codeAfter]/;!TrueQ[inFunction]]。因为如果你不这样做,并且你将这个技巧用于多个功能并且忘记使用不同的$inMsg 符号(或者它们只是碰巧在全局范围内定义),那么你就是 very 微妙的错误,我只希望调试愉快。在定义时使用Module 将保证该符号对于每个函数都是唯一的,并且不容易在全球范围内使用。
    • @belisarius 您可以做的一件事是定义三个函数 - redefredefOn,redefOff,并让它们共享一个本地符号。调用redef[f,codeBefore,codeAfter]redef 必须是HoldRestHoldAll)将生成f 的修改定义,如上所示,还有另外两个(都在外部Module 内):redefOn[f]:=inFunction = True; redefOff[f]:=inFunction = False。这样,您以受控方式将Module- 生成的触发器变量公开给顶层。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 1970-01-01
    • 2011-05-24
    相关资源
    最近更新 更多