определить: не допускается в контексте выражения - PullRequest
0 голосов
/ 05 ноября 2018

Я только начал изучать ракетку.

Я написал эту процедуру:

#lang racket

(define split
   (lambda (list)
      (define plus-list '())
      (define minus-list '())

      (cond ((null? list) '())
            (else
               (do ([i (length list) (- i 1)])
                   ((zero? i))
                   (define l (list-ref list i))
                   (define item (last-element-on-list l))

                   (cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
                         ((= (cdr l '-)) (set! minus-list (cons list minus-list))))
               )

               (cons plus-list minus-list)
            )
      )
   )
)

И вместо использования (list-ref lst i) внутри de do я определил переменную l:

(define (list-ref lst i) l)

Но, похоже, я не могу этого сделать, потому что я получаю ошибку:

define: не разрешено в контексте выражения в: (define l (list-ref lst i))

Но внутри do.

много define.

Если я удалю все определения внутри do, мне придется написать много кода, и его будет не так легко прочитать и понять:

(define split
   (lambda (list)
      (define plus-list '())
      (define minus-list '())

      (cond ((null? list) '())
            (else
               (do ([i (length list) (- i 1)])
                   ((zero? i))

                   (cond ((= (cdr (last-element-on-list (list-ref list i)) '+)) (set! plus-list (cons (list-ref list i) plus-list)))
                         ((= (cdr (last-element-on-list (list-ref list i)) '-)) (set! minus-list (cons (list-ref list i) minus-list))))
               )

               (cons plus-list minus-list)
            )
      )
   )
)

Как я могу определить переменную внутри do?

Ответы [ 3 ]

0 голосов
/ 05 ноября 2018

Чтение ваш другой вопрос Я понимаю, почему вы пишете выражения полужирным шрифтом -

…

(cond (<b>(= (cdr (last-element-on-list (list-ref list i)) '+)</b>)
       (set! plus-list
             (cons (list-ref list i) plus-list)))

      (<b>(= (cdr (last-element-on-list (list-ref list i)) '-)</b>)
       (set! minus-list
             (cons (list-ref list i) minus-list))))
&hellip;

Показан ваш входной список -

(define lst
  '((n 25 f +)
    (s 25 m +)
    (ll 20 no -)))

Ваш split проверяет содержимое каждого элемента l. split превысил свои границы, и теперь он only работает для списков, содержащих элементы этой конкретной структуры. Наряду с set! отсутствие else в cond обычно указывает на то, что вы делаете что-то не так. Вы также звоните (cdr (last-element-of-list ...)). Если last-element-of-list возвращает атом, cdr выдаст здесь ошибку.

Рассмотрим проектирование split в более общем виде -

(define (split proc l)
  (define (loop l true false)
    (cond ((null? l)
           (cons true false))
          ((proc (car l))
           (loop (cdr l)
                 (cons (car l) true)
                 false))
          (else
           (loop (cdr l)
                 true
                 (cons (car l) false)))))
  (loop l '() '()))

(split (lambda (x) (> x 5))
       '(1 5 3 9 7 0 8 3 2 6 4))

;; '((6 8 7 9) 4 2 3 0 3 5 1)

Если наш список содержит разные элементы, мы все равно можем использовать ту же самую процедуру split -

(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +)))
;; '(((4 +) (3 +) (2 +) (1 +)) (3 -) (2 -) (1 -))

Я думаю, никогда не рано начинать изучение стиля прохождения продолжения. Ниже return представляет наше продолжение и по умолчанию cons, ту же процедуру, которую мы использовали для возврата окончательного результата в нашей первоначальной реализации. Интуитивно понятно, что продолжение представляет «следующий шаг» вычисления -

(define (split proc l (return cons)) ;; `return` is our continuation
  (if (null? l)
      ;; base case: list is empty, return empty result
      (return '() '())
      ;; inductive case: at least one `x`
      (let* ((x (car l))
             (bool (proc x)))
        (split proc          ;; tail recur with our proc
               (cdr l)       ;; ... a smaller list
               (lambda (t f) ;; ... and "the next step"
                 (if bool                        ;; if `(proc x)` returned true
                     (return (cons x t)          ;; ... cons the `x` onto the `t` result
                             f)                  ;; ... and leave the `f` the same
                     (return t                   ;; otherwise leave `t` the same
                             (cons x f))))))))   ;; ... and cons the `x` onto the `f` result

Если мы запустим нашу split процедуру, вы заметите, что мы получаем точно такой же вывод, как и выше. На первый взгляд кажется, что мы напортачили с хорошей программой, однако есть одно явное преимущество этой реализации. Поскольку продолжение настраивается пользователем, вместо cons мы могли бы решить совершенно другую судьбу для наших двух списков, t и f -

(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
       (lambda (plus minus)
          (printf "plus: ~a, minus: ~a\n" plus minus)))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))

Обратите внимание, как plus и minus дали соответствующие результаты. Нам не нужно было выделять промежуточный результат cons. Более интуитивно, мы хотим, чтобы printf был «следующим шагом» , но нам нужно только указать первый аргумент -

(split (lambda (x) (eq? '+ (cadr x)))
       '((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
       (curry printf "plus: ~a, minus: ~a\n"))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))

Теперь мы поцарапали поверхность функционального стиля: D

0 голосов
/ 07 ноября 2018

do петли не являются идиоматическими ракетками. Они унаследованы от Схемы и по любой причине не допускают внутренних определений. Я никогда не использовал цикл do в Racket, так как понимания for более функциональны, и с ними просто легче работать. Кроме того, поскольку они происходят из Racket, а не из Scheme, они поддерживают внутренние определения, как и следовало ожидать.

Вы можете написать свою функцию split, используя for/fold вместо do, что дает дополнительное преимущество: нет необходимости использовать set! (и избегать квадратичного времени доступа при использовании list-ref вместо итерации по список). Я не совсем уверен, что должна делать ваша split функция, поскольку даже после удаления внутреннего определения она не компилируется, но вот мое лучшее предположение о том, что вы, возможно, пытаетесь сделать:

(define (split lst)
  (for/fold ([plus-lst '()]
             [minus-lst '()])
            ([l (in-list lst)])
    (define item (last l))
    (cond
      [(equal? item '+)
       (values (cons l plus-lst) minus-lst)]
      [(equal? item '-)
       (values plus-lst (cons l minus-lst))]
      [else
       (values plus-lst minus-lst)])))

Помимо очевидной реструктуризации использования for/fold вместо do, этот код также вносит следующие изменения в ваш код:

  1. Он использует встроенную функцию last из racket/list, чтобы получить последний элемент списка.

  2. Для сравнения символов используется equal? вместо =, поскольку = специально для сравнения чисел.

  3. Правильно отступает и ставит закрывающие скобки в идиоматических местах.

0 голосов
/ 05 ноября 2018

Я исправил ваш код, используя let, прочитайте документацию о let, он интенсивно используется в Scheme / Racket. В последнее время я не пользовался Схемой, поэтому не мог объяснить ее так же, как в документации.

Вскоре это локальное определение / переопределение символа, и вы можете использовать символ со значением только в let теле.

Краткий пример на let

(define x 5)

(let ((x 10))
  (display x)) # => 10

(display x) # => 5

(let ((y 1))
  (display y)) # => 1

(display y) # = => (error)  y: undefined

Ваш код исправлен с помощью let

(define split
(lambda (list)
 (let ((plus-list '())
        (minus-list '()))

  (cond ((null? list) '())
        (else
           (do ([i (length list) (- i 1)])
               ((zero? i))
               (let ((l (list-ref list i))
                     (item (last-element-on-list l)))

               (cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
                     ((= (cdr l '-)) (set! minus-list (cons list minus-list))))))
           (cons plus-list minus-list))))))
...