2014-01-03 1 views
3

Я хочу определить константу foo, используя вспомогательную функцию, скажем, bar. И я хочу, чтобы скрыть bar внутри определения foo, так что я пришел с этим кодом:достойный способ вложенного определения в схеме

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

Однако это определение вызывает ошибки синтаксиса во многих реализациях схемы (мит-схему, рэкет, коварство и т.д.).

У меня есть три обходные пути, но ни один из них не кажется удовлетворительным:

(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 использует лямбда, чтобы создать среду написания вспомогательных определений и скобки, кажется, как-то сбивает с толку.

foo2 использует пусть выражение, но я больше не могу использовать синтаксический сахар (define (f n) ...) =>(define f (lambda (n) ...))

foo3 требует меньше изменений по сравнению с исходным, но каждый раз, когда я хочу это значение, я должен позвонить (foo3) и сделать вычисление снова и снова.

Моих вопросов:

  1. Я думаю, что этот вид вложенного определения имеет смысл, но почему это считается ошибкой синтаксиса?
  2. Есть ли достойный способ написать определение foo?
+1

Оба 'foo1' и' foo2 'эквивалентны (' let' использует 'lambda' под капотом). С другой стороны, 'foo3' определяет новую функцию, а не постоянное значение как другие параметры, поэтому она не эквивалентна. –

+0

стандартный и правильный способ перезаписи вложенных 'define' (которые * являются * рекурсивными) с 'letrec':' (define foo (letrec ((bar (lambda (n) (+ nn)))) (bar 1))) '. , который эквивалентен (более популярному) * "named' let' "* construct,' (define foo (let bar ((n 1)) (+ nn))) '(даже если' bar' не называется рекурсивно здесь вообще). –

+0

(определите foo1 ((lambda() (определите (бар n)) (+ n n)) (бар 1)))), это превысило скобки, удалите одну пару круглых скобок вокруг лямбда; подобный это (определить foo1 (лямбда() (определяем (bar n) (+ n n)) (балка 1))). Оно работает. – Alfgaar

ответ

2

Ответ на ваши вопросы:

  1. define могут быть использованы только определенным образом, в соответствии с мандатом specification. То, что вы хотите сделать, не покрывается спецификацией, поэтому ошибка. Как вы знаете, define присваивает имя значению выражения, просто вы не можете напрямую создавать внутренние определения в своем контексте.
  2. Но есть и другие выражения, которые позволяют создавать новые привязки в этом контексте. IMHO foo2 - лучший вариант здесь, и это тоже идиоматично. А если bar были рекурсивным определением, вы можете использовать letrec.

Но если потери немного синтаксического сахара беспокоит вас (из-процедур образом определяются внутри выражения let), а затем попробуйте использовать local, он будет работать в Ракетка:

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

foo1 также эквивалентно следующему:

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

Насколько это более приемлемо-смотря на вас?

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; вместо этого вы будете использовать подмодуль.

4

Ваш исходный код имеет синтаксическую ошибку, так как необходимый синтаксис для define идентификатора является

(define <identifier> <expression>) 

но ваш синтаксис

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

Вы должны каким-то образом к группе <definition> и <expression> , То, что вы ищете, - это то, что позволяет лексические определения - в Схеме это синтаксическая форма с <body>. Синтаксическими формами для этого являются lambda или любые let (и варианты) или «программные» begin.

Но это легко сделать на Схеме без необходимости расширения Racket или дополнительных, пустых лексических сред или синтаксической формы <body>. Просто используйте то, что вы считали 'неудовлетворительно'

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

или даже

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

Слишком много сахара, даже синтаксический сахар, мой иметь неблагоприятные последствия для здоровья ...

+2

«Синтаксический сахар вызывает рак точки с запятой». Алан Дж. Перлис, эпиграмма 3. http://www.cs.yale.edu/quotes.html –

Смежные вопросы