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))
现在 oops
是 secret
。解决此问题的一种方法是使用 make-base-namespace
获取等效于 racket/base
.
的命名空间
我有一个列表,其中包含一些函数的“名称”(例如 '(+ - *)
)
我想用那个列表来调用函数
(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))
现在 oops
是 secret
。解决此问题的一种方法是使用 make-base-namespace
获取等效于 racket/base
.