Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在公共Lisp中模拟Clojure样式的可调用对象_Clojure_Common Lisp_Lisp 2_Callable Object - Fatal编程技术网

在公共Lisp中模拟Clojure样式的可调用对象

在公共Lisp中模拟Clojure样式的可调用对象,clojure,common-lisp,lisp-2,callable-object,Clojure,Common Lisp,Lisp 2,Callable Object,在Clojure中,哈希映射和向量实现调用,因此它们可以用作函数 (let [dict {:species "Ursus horribilis" :ornery :true :diet "You"}] (dict :diet)) lein> "You" 或者,对于向量 (let [v [42 613 28]] (v 1)) lein> 613 通过让Clojure中的可调用对象实现IFn,可以在Clojure中创建这些对

在Clojure中,哈希映射和向量实现
调用
,因此它们可以用作函数

(let [dict {:species "Ursus horribilis"
            :ornery :true
            :diet "You"}]
  (dict :diet))

lein> "You"
或者,对于向量

(let [v [42 613 28]]
  (v 1))

lein> 613
通过让Clojure中的可调用对象实现
IFn
,可以在Clojure中创建这些对象。我对Common Lisp还比较陌生--是否可以调用对象,如果可以,那么实现它需要什么呢?我真的希望能够做到以下几点

(let ((A (make-array (list n n) ...)))
   (loop for i from 0 to n
         for j from 0 to m
      do (setf (A i j) (something i j)))
   A)
而不是将代码与aref混为一谈。同样,如果您能够以同样的方式访问其他数据结构的条目,例如字典,那就太酷了


我已经研究了Lisp/Scheme中的函数对象,似乎拥有一个单独的函数名称空间会使CL的问题复杂化,而在Scheme中,您可以通过闭包来实现这一点。

公共Lisp的前身中的可调用对象示例

以前提供过可调用对象。例如,在Lisp机器Lisp中:

公共Lisp中的绑定

Common Lisp有单独的函数和值名称空间。因此
(array 10 1 20)
只有在
array
是表示函数名称空间中函数的符号时才有意义。因此,函数值将是一个可调用的数组

将绑定到变量的值作为函数使用,这在很大程度上违背了函数和值使用不同名称空间的目的

(let ((v #(1 2 3)))          
  (v 10))                    ; doesn't work in Common Lisp
在函数和值具有不同名称空间的语言中,上述内容毫无意义

(let ((v #(1 2 3)))          
  (v 10))                    ; doesn't work in Common Lisp
FLET
用于函数,而不是
LET

(flet ((v #(1 2 3 4 5 6 7))) ; doesn't work in Common Lisp
  (v 4))                     
这意味着我们将把数据放入函数名称空间。我们想要吗?不是真的

文本数据作为函数调用中的函数。

还可以考虑至少允许文字数据在直接函数调用中充当函数:

(#(1 2 3 4 5 6 7) 4)         ; doesn't work in Common Lisp
而不是

(aref #(1 2 3 4 5 6 7) 4)
CommonLisp不允许以任何琐碎或相对简单的方式实现这一点

旁注:

我们可以在将函数和值与CLO集成的方向上实现一些东西,因为CLO通用函数也是类
STANDARD-generic-FUNCTION
的CLOS实例,并且可以拥有和使用该类的用户定义子类。但这通常不会被利用

建议


因此,最好调整到不同的语言风格,并按原样使用CL。在这种情况下,CommonLisp不够灵活,无法轻松地合并这样的功能。对于较小的代码优化,不省略符号是一般的CL风格。危险在于混淆和只写代码,因为很多信息都不是直接出现在源代码中。

尽管可能没有一种方法可以准确地完成您想要做的事情,但也有一些方法可以将类似的事情拼凑在一起。一个选项是定义一个新的绑定表单,使用callable,它允许我们将函数本地绑定到可调用对象。例如,我们可以

(with-callable ((x (make-array ...)))
  (x ...))
大致相当于

(let ((x (make-array ...)))
  (aref x ...))
以下是可调用的的的可能定义:

(defmacro with-callable (bindings &body body)
  "For each binding that contains a name and an expression, bind the
   name to a local function which will be a callable form of the
   value of the expression."
  (let ((gensyms (loop for b in bindings collect (gensym))))
    `(let ,(loop for (var val) in bindings
                 for g in gensyms
                 collect `(,g (make-callable ,val)))
       (flet ,(loop for (var val) in bindings
                    for g in gensyms
                    collect `(,var (&rest args) (apply ,g args)))
         ,@body))))
剩下的就是为make callable定义不同的方法,这些方法返回用于访问对象的闭包。例如,这里有一个为数组定义它的方法:

(defmethod make-callable ((obj array))
  "Make an array callable."
  (lambda (&rest indices)
    (apply #'aref obj indices)))
由于这种语法有点难看,我们可以使用宏使其更漂亮

(defmacro defcallable (type args &body body)
  "Define how a callable form of TYPE should get access into it."
  `(defmethod make-callable ((,(car args) ,type))
     ,(format nil "Make a ~A callable." type)
     (lambda ,(cdr args) ,@body)))
现在,为了使数组可调用,我们将使用:

(defcallable array (obj &rest indicies)
  (apply #'aref obj indicies))
好多了。我们现在有了一个表单,带有callable,它将定义允许我们访问对象的本地函数,还有一个宏defcallable,它允许我们定义如何创建其他类型的可调用版本。这种策略的一个缺陷是,每当我们想要使对象可调用时,都必须显式地使用with callable


另一个类似于可调用对象的选项是Arc的结构访问。基本上x.5访问x中索引5处的元素。我能够在CommonLisp中实现这一点。你可以看到我为它写的代码,还有。我也对它进行了测试,这样你就可以看到使用它是什么样子了


我的实现是如何工作的,我编写了一个宏w/ssyntax,它查看主体中的所有符号,并为其中一些定义宏和符号宏。例如,x.5的符号宏是(get x 5),其中get是我定义的访问结构的通用函数。这方面的缺陷是,我总是必须在任何我想使用ssyntax的地方使用w/ssyntax。幸运的是,我能够将它隐藏在一个类似于defun的宏def中。

我同意Rainer Joswig的建议:最好熟悉Common Lisp的做事方式——就像普通Lisp程序员在切换到Clojure时熟悉Clojure的做事方式一样。然而,正如malisper复杂的回答所显示的,你可以做你想做的部分事情。下面是一个更简单策略的开始:

(defun make-array-fn (a) 
  "Return a function that, when passed an integer i, will 
  return the element of array a at index i."
  (lambda (i) (aref a i)))

(setf (symbol-function 'foo) (make-array-fn #(4 5 6)))

(foo 0) ; => 4
(foo 1) ; => 5
(foo 2) ; => 6
symbol function
访问符号的函数单元
foo
,并
setf
make array fn
创建的函数对象放入其中。由于该函数随后位于函数单元格中,
foo
可用于列表的函数位置。如果需要,可以将整个操作包装到宏中,例如:

(defmacro def-array-fn (sym a)
  "Define sym as a function that is the result of (make-array-fn a)."
  `(setf (symbol-function ',sym)
         (make-array-fn ,a)))

(def-array-fn bar #(10 20 30 40))

(bar 0) ; => 10
(bar 1) ; => 20
(bar 3) ; => 40

当然,以这种方式定义的“数组”看起来不再像数组。我想你可以用CL的打印程序做些有趣的事情。也可以允许设置数组的值,但这可能需要单独的符号。

实际上,在Clojure中,这超出了名称空间的分隔。以下内容在Clojure中可以正常工作:
({:a“first key”:b“second key”}:a)
将产生
“first key”
,因此Clojure在这方面的行为类似于Lisp Machine Lisp。因此,是的,在Clojure中,对于某些类型,“数据在函数名称空间中”。“我们想要吗?”显然,korrok对此的回答是肯定的,但Co