如何调用多个对象的多个方法

时间:2014-08-05 20:50:23

标签: racket

假设我有一个班级:

(class object%
  (init val)
  (define/public (get-val) ... )
  (define/public (get-norm) ... ))

为什么我有两个实例foobar我可以这样做:

(eval '(send foo get-val))

但不能这样做:

(for/list ([who  '(foo bar)])
  (for/list ([what '(get-val get-norm)])
    (eval (cons 'send (list who what)))))

在后一种情况下,我收到错误:

send: unbound identifier;
 also, no #%app syntax transformer is bound in: send

获得我想要的结果的好方法是什么?

UPDATE:我发现嵌套循环在交互模式下工作正常,但如果在定义区域中写入则会产生错误。任何人都可以解释一下这个诀窍吗?

2 个答案:

答案 0 :(得分:2)

您可以使用:

(for*/list ([who (list foo bar)]
            [what '(get-val get-norm)])
  (dynamic-send who what))

原因是'(foo bar)是包含两个符号的列表foobar,而不是包含变量值的列表foobar;你必须使用(list foo bar)

此外,为了能够send变量中的方法名称,而不是直接命名方法,您必须使用dynamic-send

请注意,我的代码不使用eval,您也不应该使用eval。大多数情况下,您永远不需要使用{{1}},并且尽可能避免使用它。

答案 1 :(得分:2)

问题在于使用eval。在REPL中使用eval工作正常,因为REPL为eval提供了racket的所有导出的命名空间。 REPL本质上是先运行模块,然后创建一个包含所有结果定义的新命名空间,并在使用该命名空间输入的任何内容上调用eval,因此在调用send时定义了eval 。但是,在定义窗口中,您需要自己为eval提供命名空间,以使其正常工作,因为初始当前命名空间为空。所以你可以做的是将它添加到定义的开头:

(define ns (make-base-namespace))

现在,无论何时使用eval,都将ns作为命名空间参数传递,如(eval '(+ 1 2) ns中所示。但是有很多警告。

  1. 这是单独的命名空间。您在模块中定义的任何内容都不会在使用此命名空间评估的任何内容中显示。如果您(define foo 3)(eval '(+ 1 foo) ns)无法工作 - foo未在ns命名空间中定义,则会在当前模块中定义命名空间。
  2. 默认情况下,此命名空间仅导入racket/base。你必须做一些eval-shenanigans或一些命名空间 - 恶作剧来要求racket/class
  3. 你可以传递eval (current-namespace)参数的值,但这附带了它自己特别注意的警告和复杂性。
  4. 名称空间之间的通信并不有趣。这通常是模块的用途。
  5. 核心问题是eval非常非常非常适合工作,更不用说最好的工具了。在你不知道发生了什么的情况下,有许多角落的情况和陷阱可能会蔓延到你身上,更不用说可能会恶化的性能问题了。除非你真的知道自己在做什么,并确信那里没有更好的选择,否则就像瘟疫一样避免它。

    请问您为什么需要在问题中使用foo处理bareval

    修改

    有关eval的更多信息,请参阅the Racket documentation,这是非常彻底且有用的资源。