【问题标题】:Syntax sugar for funcall?funcall 的语法糖?
【发布时间】:2014-07-01 11:02:23
【问题描述】:

为什么 funcall 没有语法糖?我们非常需要它。写 (&#do-something arg0 arg1 arg2) 而不是 (funcall do-something arg0 arg1 arg2) 这样的东西不是很好吗 为什么不添加像 Ruby 的 &:method 表示法这样的东西?该语言中是否已经存在某些内容?

【问题讨论】:

    标签: functional-programming lisp common-lisp


    【解决方案1】:

    语言中没有类似的东西;不过,您可以轻松地将函数分配给其他名称,然后使用它。例如,如果你想使用&,你可以:

    CL-USER> (setf (symbol-function '&) (symbol-function 'funcall))
    #<FUNCTION FUNCALL>
    CL-USER> (&'list 1 2 3)
    (1 2 3)
    CL-USER> (&'cons 'x 'y)
    (X . Y)
    

    (最近 Stack Overflow 上有一个关于第一行的不同方法的问题:How to stop evaluating lisp form when passed as function parameter?。)

    (&amp;#do-something arg0 arg1 arg2)而不是(funcall do-something arg0 arg1 arg2)不是很好吗?

    可能不会。 Common Lisp 是一种语言,它更喜欢函数和宏的富有表现力的名称。名称 funcall 被识别,它清楚地表明发生了间接函数调用。为什么我们要掩盖它?显然,大多数人发现 funcall 在“短到可以写”和“长到可以描述”之间取得了适当的平衡。

    为什么 funcall 没有语法糖?

    您可以在 Common Lisp 中将宏称为“语法糖”,但这里不需要它。 Funcall 是一个函数,而不是特殊的形式或宏,因此用另一个名称对其进行别名真的非常容易,如上所示。大多数人这样做的事实应该说明这是多么令人向往。

    【讨论】:

      【解决方案2】:

      funcall 没有语法,因为 我们 不需要它很多(或者,定义“我们”)。我的意思是,在诸如 Common Lisp 之类的 Lisp-2 中,这是一种权衡,除非你正在做一些繁重的 lambda 演算,否则你会习惯它。

      如果你真的想使用更简洁的语法,你可以定义一个dispatch macro character

      (set-dispatch-macro-character
       #\# #\&
       #'(lambda (stream subchar arg)
           (let ((args-var (gensym)))
             `(lambda (&rest ,args-var)
                (declare (dynamic-extent ,args-var))
                (apply ,(read stream t nil t) ,args-var)))))
      
      (let ((my-function #'max))
        (#&my-function 1 3 2))
      

      检查您的编译器是否会优化lambda formsdynamic-extent &amp;rest 参数传递给apply

      我建议不要将单个字符 &amp; 定义为 macro character,因为它是 constituent character,主要用于符号 &amp;optional, &amp;rest, &amp;key, &amp;allow-other-keys, &amp;aux, &amp;whole, &amp;body and &amp;environment

      【讨论】:

      • 漂亮!我考虑过 lambda 包装,但决定将 funcall 放在前面。 +1
      • 您的选项,#! 在整个表单后面,也很聪明,它不会依赖于优化 lambda 表单(和 apply),自动避免一次调用/间接调用。跨度>
      • 不过,将&amp; 定义为函数没有问题,而且输入的数量相同:(&amp; my-function 1 3 2),并且也适用于符号:(&amp;'list 1 2 3)。相同数量的输入没有应用的开销,也没有调度宏字符的复杂性。
      • 你是对的,它可以是inline 和/或有一个编译器宏。
      【解决方案3】:

      如果你喜欢黑魔法:

      (defun black-magic (stream sub-char numarg)
        (declare (ignore sub-char numarg))
        `(funcall ,@(read stream)))
      
      (set-dispatch-macro-character
        #\# #\! #'black-magic)
      

      粗略的例子:

      CL-USER> (defun foo (fn x y z)
                 #!(fn x y z))
      FOO
      CL-USER> (foo #'* 1 2 3)
      6
      CL-USER> (foo #'+ 1 1 1)
      3
      

      (不过,最好使用apply...但最好保持简单,不使用此类。)

      当然,我们任何人都可以通过阅读器宏定义类似的东西,但我认为如果你期望有人会在某个时候阅读你的代码,那么这样做并不是一个好主意。

      实际上,这种阅读器宏是您需要的语法糖。你现在拒绝自己的想法吗?每次,当您使用非传统语法糖时,您可能会让未来的代码阅读者变得更糟。

      【讨论】:

      • 这是这种宏的一个很好的例子,尽管它实际上并没有保存任何输入,而不是一个字符的函数名称。例如,#!(list 1 2 3) 不比 (!'list 1 2 3) 短,#!(f x y)(! f x y) 也是如此。
      • @Joshua,说实话,我看不出引入这种语法糖的理由,我总是会使用更长和标准的(funcall f x y),我发布这个选项只是作为一个例子这种事情是可能的。
      猜你喜欢
      • 2013-07-05
      • 2011-04-21
      • 1970-01-01
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 2011-08-02
      • 2010-09-15
      相关资源
      最近更新 更多