我想使用辅助函数定义一个常量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)
并重新进行计算。
我的问题是:
foo
?答案 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)
回答你的问题:
define
只能按照specification的要求以某种方式使用。您想要做的事情不在规范范围内,因此错误。如您所知,define
为表达式的值指定了一个名称,只是您无法在其上下文中直接创建内部定义。foo2
是这里最好的选择,它也是惯用的。如果bar
是递归定义,则可以使用letrec
。但是如果失去一些语法糖会困扰你(因为在let
表达式中定义了程序的方式),那么尝试使用local
,它将在Racket中起作用:
(define foo
(local [(define (bar n) (+ n n))]
(bar 1)))