方案中嵌套定义的体面方式

时间:2014-01-03 04:18:04

标签: scheme racket mit-scheme guile

我想使用辅助函数定义一个常量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
  3. 的定义

4 个答案:

答案 0 :(得分: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这样的私人助手可能在定义多个其他函数或常量时很有用。

  • 通常文件表单更方便:如果bar之类的帮助者嵌套,而是在foo.rkt的模块顶层,则更容易在REPL中调试它。

P.S。 Racket提供define-package形式以与Chez Scheme兼容,但它在Racket中不是惯用的;相反,你会使用一个子模块。

答案 1 :(得分:4)

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

(define <identifier> <expression>)

但你的语法是

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

您需要某种方法对<definition><expression>进行分组。您正在寻找的是允许词汇定义的东西 - 在Scheme中,这是一个带有<body>的句法形式。这个句法形式是lambda或任何let(和变体)或'程序化'begin

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

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

甚至

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

过多的糖,甚至是语法糖,对我的健康都有不良后果......

答案 2 :(得分:3)

foo1也等同于以下内容:

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

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

答案 3 :(得分:2)

回答你的问题:

  1. define只能按照specification的要求以某种方式使用。您想要做的事情不在规范范围内,因此错误。如您所知,define为表达式的值指定了一个名称,只是您无法在其上下文中直接创建内部定义。
  2. 但是还有其他表达式允许在此上下文中创建新绑定。恕我直言foo2是这里最好的选择,它也是惯用的。如果bar是递归定义,则可以使用letrec
  3. 但是如果失去一些语法糖会困扰你(因为在let表达式中定义了程序的方式),那么尝试使用local,它将在Racket中起作用:

    (define foo
      (local [(define (bar n) (+ n n))]
        (bar 1)))