简体   繁体   English

在Racket中定义出错

[英]Error with define in Racket

I just discovered Racket a few days ago, and I'm trying to get more comfortable with it by writing a little script that generates images to represent source code using #lang slideshow . 我几天前刚刚发现了Racket,我试图通过编写一个生成图像来表示使用#lang slideshow源代码的#lang slideshow本来更加熟悉它。

I know that when programming in a functional paradigm it's good practice to create almost all your variables with let , but I find that it introduces too many levels of nesting and that Racket's let has an overcomplicated API which requires superfluous parentheses. 我知道在使用函数范例进行编程时,最好用let创建几乎所有变量,但我发现它引入了太多级别的嵌套,并且Racket的let有一个过于复杂的API,需要多余的括号。 I'm sure this is to remove ambiguity when using let in more powerful ways, but for my purposes it's just an annoyance. 我确信这是为了消除在更强大的方式下使用let时的歧义,但就我的目的来说,这只是一个烦恼。 Consequently, I'm creating all my variables with define , and writing blocks with begin if I need to (such as in the body of an if statement). 因此,我正在使用define创建所有变量,并在需要时使用begin编写块(例如在if语句的主体中)。

The problem is that I've repeatedly been getting what seem to be very mysterious errors. 问题是我一再得到看似非常神秘的错误。 I'm sure I'm just making some silly beginner's mistake, being new to the language, but I really can't seem to find the source of the complaint. 我确定我只是犯了一些愚蠢的初学者的错误,对语言不熟悉,但我似乎无法找到投诉的来源。

Here's the offending code: 这是有问题的代码:

(define sub-code (foldr ht-append (rectangle 0 0) (map internal-style (rest code))))

although what we're defining sub-code to seems pretty irrelevant. 虽然我们定义的sub-code似乎非常无关紧要。 If I replace it with 如果我用它替换它

(define sub-code '())

I receive the same error. 我收到同样的错误。 DrRacket is saying that define is being used in an expression context. DrRacket说,在表达式上下文中使用了define I understand what this error would normally mean - IE that it would raise when you write code like (print (define x 10)) , but I can't see what would trigger it here. 我理解这个错误通常意味着什么 - 当你编写像(print (define x 10))这样的代码时它会引发IE,但是我看不到会在这里触发什么。

If it helps, this define is at the beginning of a begin block, inside an if statement 如果有帮助,则此define位于if语句内的begin块的开头

(if (list? code)
    (begin
        (define sub-code '())
        ; a few more define statements and finally an expression ))

The specific error message DrRacket is printing is DrRacket正在打印的特定错误消息是

define: not allowed in an expression context in: (define sub-code (quote ()))

I thought maybe define isn't allowed in begin blocks, but I checked the docs and one of the examples for begin is 我认为也许在begin块中不允许define ,但我检查了文档 ,其中一个示例为begin

(begin
    (define x 10)
    x)

So I don't really know what to do. 所以我真的不知道该怎么做。 Thanks in advance! 提前致谢!

Definitions are allowed in a 'body' context, like in lambda and let among others. 定义是在允许的“身体”背景下,像lambdalet等等。 The consequent and alternate clauses of if are not body contexts; if的后续和替代条款不是身体背景; they are expression contexts and therefore definitions are not allowed. 它们是表达式上下文,因此不允许定义。

begin is special - begin in a body context allows definitions, but begin in an expression contexts forbids definitions. begin是特殊的 - 在body上下文中begin允许定义,但是begin表达式上下文begin禁止定义。 Your case falls in to the later. 你的案子属于后者。

For example: 例如:

(define (foo . args)     #| body context #|)
(define foo (lambda args #| body context |#))
(define (foo . args)
  (let (...)
    #| body context |#))

Syntactic keywords that requires expressions: if, cond, case, and, or, when, unless, do, begin . 需要表达式的语法关键字: if, cond, case, and, or, when, unless, do, begin Check out the formal syntax in any Scheme report (r{4,5,6,7}rs); 查看任何Scheme报告中的正式语法(r {4,5,6,7} rs); look for <body> , <sequence> , <command> , and <expression> . 查找<body><sequence><command><expression>

Also, if you need a body context in an expression, just wrap a let syntactic form, as such: 此外,如果您需要表达式中的正文上下文,只需包含一个let语法表单,如下所示:

(if test
    (let ()
      (define foo 'foo)
      (list foo foo))
    alternate)

As GoZoner explained, you can't use define in an expression context. 正如GoZoner所解释的那样,你不能在表达式上下文中使用define

What could you do instead? 你能做什么呢?

Use let : 使用let

(if (list? code)
    (let ([x '()])
      x)
    ...

Or it would work with an "empty" let and define : 或者它可以使用“空” let define

(if (list? code)
    (let ()
      (define x '())
      x)
    ...

But that's a bit silly. 但那有点傻。

Or use cond and define : 或者使用conddefine

(cond [(list? code)
       (define x '())
       x]
      ...

This last way -- using cond and define -- is closest to what the current Racket style guide recommends. 最后一种方式 - 使用conddefine - 最接近当前的Racket风格指南推荐的方式。

Here's more details, from the Racket docs. 这里有更多细节,来自Racket文档。

The different contexts are required because macros must expand differently, depending on which language forms are allowed. 不同的上下文是必需的,因为宏必须以不同的方式扩展,具体取决于允许的语言形式。

As others have said, definitions are not allowed in expression contexts ("expr ..." in the docs), but are ok in other contexts. 正如其他人所说,表达式上下文中不允许定义(文档中的“expr ...”),但在其他情况下也可以。

In other doc entries, "body ..." indicates an internal-definition context ( guide , reference ), for example in lambda bodies, and "form ..." indicates all non-expression contexts, like in the docs for begin. 在其他doc条目中,“body ...”表示内部定义上下文( 指南引用 ),例如在lambda主体中,“form ...”表示所有非表达式上下文,如在begins文档中。

或者你可以将表达式包装在(开始)中,例如(开始(定义x 10)(定义y 100)(定义z 1000))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM