racket - 如何将列表的元素用作过程(函数)

racket - how to use the elements of a list as a procedure (function)

我有一个列表,其中包含一些函数的“名称”(例如 '(+ - *)

我想用那个列表来调用函数

(define list_of_names_of_functions '(+ - *))

(define sum (car list_of_names_of_functions))

(sum 2 3)

但是,sum 是一个列表,所以不能用作过程。

我应该怎么做?

刚刚找到答案,抱歉。

解决方案是使用“eval”函数

(define list_of_names_of_functions '(+ - *))

(define sum (car list_of_names_of_functions))

((eval sum) 2 3)

变量list_of_names_of_functions有符号表示而不是实际功能。您或许应该创建一个名称和功能如下的列表:

(define ops `((+ . ,+) (- . ,-) (* . ,*)))

如果您计算 ops,您会看到如下内容:

((+ . #<procedure:+>) 
 (- . #<procedure:->) 
 (* . #<procedure:*>))

没有关于如何表示 function/procedure object 的标准,但预计该值可以作为任何其他函数应用:

((cdar ops) 1 2) ; ==> 3

因此您可以使用以下方式搜索 +

(assq '+ ops)
; ==> (+ . #<procedure:+>)
(assq '/ ops)
; ==> #f

因此,如果结果为真,cdr 将包含一个过程:

(apply (cdr (assq '+ ops)) '(1 2)) ; ==> 3

如果你正在制作 eval 的特殊孩子,那么你会认为该列表是一个环境,并且可以轻松添加它,以便你可以添加 /。您还可以提供任何可调用的东西,这样主机系统中就不需要存在操作。例如。 `(square . ,(lambda (v) (* v v)) 也有效。

考虑使用 Scheme(和 Lisp)的命名约定。例如。而不是 list_of_names_of_functions 它应该被命名为 list-of-names-of-functions

简单而直接的解决方法是使用 (define list_of_functions (list+ - *)),然后使用 (define sum (car list_of_functions)) 就可以了。

(edit:) 发生这种情况是因为 list 是一个计算其参数的函数,而 returns 结果在列表中。而 quote(或表达式前面的 ')阻止对其参数进行求值。

'(A B ...)等同于(quote (A B ...))(A B ...) 是一个符号列表,而 quote 会原样保留它。

这是一个符号列表,因为这是从源文件或 REPL 中读取 Lisp 代码的方式,即在解释器的提示下。其他语言的代码作为源代码文件中的字符串,而编译器是一些完全在语言之外存在和工作的外部魔法。在类 Lisp 语言中,代码就是数据。代码是从文本源代码文件中读取的(即字符串被转换为)嵌套的符号列表,并且显式 eval 以及隐式求值深入到语言本身。

因此你所拥有的等同于列表

(define list_of_names (list '+ '- '*))

其中包含未计算的 符号 ,与列表

相反
(define list_of_functions (list + - *))

其中包含它们的 ,它们恰好是由那些“名称”表示的常用内置函数。

并且我们可以调用一个函数,但是我们不能调用一个符号(无论如何在 Scheme 中都不能)。

正如我在评论中提到的,使用 eval 不是正确的答案:它 'works' 但它打开了通往恐怖的道路。

如果你有一个像 + 这样的符号,并且你想获得它的动态值,那么 Racket 的方法是使用 namespaces。命名空间实际上是一台机器,它以与 eval 相同的方式将符号转换为值,但它是 all,与 eval 不同。不幸的是,我不太了解名称空间,但这会起作用:

(define-namespace-anchor nsa)
(define ns (namespace-anchor->namespace nsa))

(define (module-symbol-value s (in ns))
  (namespace-variable-value s #t #f in))

然后

(define function-names '(+ - / *))
(define sum (module-symbol-value (first function-names)))

现在

> (sum 1 2 3)
6
> (eq? sum +)
#t
>

这种方法的问题是它有漏洞:如果你有这样的东西:

(module msv racket
  (provide module-symbol-value)

  (define-namespace-anchor nsa)
  (define ns (namespace-anchor->namespace nsa))

  (define secret "secret")
  
  (define (module-symbol-value s (in ns))
    (namespace-variable-value s #t #f in)))

(require 'msv)

(define oops (module-symbol-value 'secret))

现在 oopssecret。解决此问题的一种方法是使用 make-base-namespace 获取等效于 racket/base.

的命名空间