3

我想foo使用一个辅助函数来定义一个常量,比如bar. 我想隐藏bar在 的定义中foo,所以我提供了以下代码:

(define foo
  (define (bar n)
    (+ n n))
  (bar 1))

然而,这个定义在许多方案实现(mit-scheme、racket、guile 等)中会导致语法错误。

我有三种解决方法,但似乎都不令人满意:

(define foo1
  ((lambda ()
     (define (bar n)
       (+ n n))
     (bar 1))))

(define foo2
  (let ((bar (lambda (n) (+ n n))))
    (bar 1)))

(define (foo3)
  (define (bar n)
    (+ n n))
  (bar 1))

foo1使用 lambda 来创建编写辅助定义的环境,括号似乎有点令人困惑。

foo2使用 let 表达式,但我不能再使用语法糖(define (f n) ...)=>(define f (lambda (n) ...))

foo3与原始值相比,需要较少的修改,但每次我想要这个值时,我都必须重新调用(foo3)并重新计算。

我的问题是:

  1. 我认为这种嵌套定义是有道理的,但为什么会被认为是语法错误呢?
  2. 有什么体面的方法来写 的定义foo吗?
4

4 回答 4

4

如果我正确理解了您的问题,那么在 Racket 中执行此操作的另一种惯用方法是使用模块。

该模块可以使用单独的文件定义:

;; foo.rkt
#lang racket
(define (bar n)
  (+ n n))
(define foo (bar 1))
(provide foo)

;; use-foo.rkt
#lang racket
(require "foo.rkt")
foo

或通过module一个文件中的表格:

#lang racket
(module 'foo-mod racket
  (define (bar n)
    (+ n n))
  (define foo (bar 1))
  (provide foo))

(require 'foo-mod)
foo

与您的示例相比,这是否简洁?当然不是。但在实践中,这种封装通常在模块粒度上工作得很好。

  • 例如bar,在定义多个其他函数或常量时,类似的私有助手可能很有用。

  • 通常文件形式更方便:如果一个 helper likebar不是嵌套的,而是在模块顶层 for foo.rkt,那么在 REPL 中调试它会更容易。

ps Racket 提供了define-package一种与 Chez Scheme 兼容的形式,但它在 Racket 中不是惯用的;相反,您将使用子模块。

于 2014-01-03T14:46:46.217 回答
4

您的原始代码有语法错误,因为define标识符所需的语法是

(define <identifier> <expression>)

但你的语法是

(define <identifier> <definition> <expression>)

您需要一些方法来对<definition>和进行分组<expression>。您正在寻找的是允许词法定义的东西 - 在 Scheme 中,这是一种带有<body>. 语法形式是 alambda或 any let(和变体)或 'programmatic' begin

但是,这很容易在 Scheme 中完成,不需要 Racket 扩展或额外的、空的词法环境或<body>句法形式。只需使用您认为“不满意”的内容

(define foo
  (let ((bar (lambda (x) (+ x x))))
    (bar 1)))

甚至

(define foo
  ((lambda (x) (+ x x)) 1))

太多的糖,甚至是语法糖,都会对健康造成不良后果......

于 2014-01-03T14:59:42.800 回答
3

foo1也等价于以下内容:

(define foo1
  (let ()
    (define (bar n)
      (+ n n))
    (bar 1)))

这对你来说更容易接受吗?

于 2014-01-03T13:50:45.770 回答
2

回答您的问题:

  1. define只能以规范要求的特定方式使用。规范未涵盖您想要做的事情,因此会出现错误。如您所知,define为表达式的值分配名称,只是您不能直接在其上下文中创建内部定义。
  2. 但是还有其他表达式允许在此上下文中创建新绑定。恕我直言foo2,这里是最好的选择,也是惯用的。如果bar是递归定义,您可以使用letrec.

但是,如果丢失一些语法糖会让您感到困扰(因为过程是在let表达式中定义的方式),那么尝试使用local,它将在 Racket 中工作:

(define foo
  (local [(define (bar n) (+ n n))]
    (bar 1)))
于 2014-01-03T04:54:26.130 回答