2016-08-04 8 views
2

Я написал код, который использует ленивую оценку для создания бесконечной структуры данных, но есть ошибка.Как использовать Lazy Evaluation в схеме Guile?

Вот код:

#!/usr/bin/guile \ 
-e main -s 
!# 
(define ints-f 
    (lambda (n) 
     (let ((n1 (delay (ints-f (+ n 1))))) 
     (cons n (force n1))))) 
(define (main args) 
    (display (car (ints-f 3))) 
    (newline) 
    ) 

Это дает ошибку, которая говорит, что переполнение стека. Это означает, что сила выполняется, даже если она не вызвана. Как это исправить?

#!/usr/bin/guile \ 
-e main -s 
!# 
(define ints-f 
    (lambda (n) 
     (let ((n1 (delay (ints-f (+ n 1))))) 
     (cons n n1)))) 
(define (main args) 
    (display (car (ints-f 3))) 
    (newline) 
    ) 

Приведенный выше код дает ожидаемый выход 3, но если использовать корд, как в коде ниже

#!/usr/bin/guile \ 
-e main -s 
!# 
(define ints-f 
    (lambda (n) 
     (let ((n1 (delay (ints-f (+ n 1))))) 
     (cons n n1)))) 
(define (main args) 
    (display (cdr (ints-f 3))) 
    (newline) 
    ) 

Он печатает объект обещание.

Как использовать ленивую оценку в схеме хитрости?

+1

Если вы «задерживаете», а затем «заставляете» сразу после этого, вы обходите ленивые свойства обещаний. Форма 'delay' эффективно создает thunk, и' force' вызывает его.Единственное отличие заключается в том, что обещает кешировать их результаты, поэтому одновременное повторение одного и того же обещания не будет переоценивать вычисление. Семантика общего языка по-прежнему абсолютно строгая. –

ответ

1

Первый фрагмент неверен, вы вынуждаете значение при построении последовательности, следовательно, побеждая целую цель ленивой оценки. С другой стороны, второй фрагмент выглядит правильно - хотя это может быть немного упрощен:

(define (ints-f n) 
    (cons n (delay (ints-f (+ n 1))))) 

Это нормально получить обещание при вызове cdr, это преобразователь построен с delay, что только выход его значение когда force d. В самом деле, если вы хотите заглянуть в элементы бесконечной последовательности, вы должны будете использовать процедуру обхода части вы заинтересованы в:

(define (take seq n) 
    (if (= n 0) 
     '() 
     (cons (car seq) 
      (take (force (cdr seq)) (- n 1))))) 

Аналогично:

(define (get seq idx) 
    (if (= idx 0) 
     (car seq) 
     (get (force (cdr seq)) (- idx 1)))) 

Например:

(take (ints-f 5) 5) 
=> '(5 6 7 8 9) 

(get (ints-f 5) 5) 
=> 10 
+0

ваш 'take' делает классический надзор за чрезмерным форсированием последовательности одним дополнительным элементом. '(take seq 1)' должно быть эквивалентно '(list (car seq))', а не '(begin (force (cdr seq)) (list (car seq)))'. :) –

1

Вы пытаетесь создать потоки? Вы можете обратиться к модулю (srfi srfi-41) для его реализации. (Раскрытие информации:. Я написал Коварство специфические части кода модуля, а все остальное было портированы из the reference implementation)

(use-modules (srfi srfi-41)) 
(define-stream (ints-f n) 
    (stream-cons n (ints-f (1+ n)))) 

Обратите внимание, что define-stream и stream-cons макросы, которые работают вместе, чтобы построить (SRFI 45 -Style) delay/force за кулисами.

Пример использования:

> (stream->list 10 (ints-f 100)) 
(100 101 102 103 104 105 106 107 108 109) 

В частности, ваша функция расширяется на что-то вроде:

(define (ints-f n) 
    (lazy (eager (cons (delay n) 
        (lazy (ints-f (1+ n))))))) 

, который можно использовать с помощью:

> (define x (ints-f 100)) 
> (force (car (force x))) 
100 
> (force (car (force (cdr (force x))))) 
101 
+0

«расширяет», или «расширится, если переписано с помощью« stream-cons »? Функция OP делает простой '(cons n (delay ...))', да? –

+0

@WillNess '(cons ... (delay ...))' создает нечетные потоки, тогда как '(delay (cons ...))' создает четные потоки. Проблема с нечетными потоками заключается в том, что вы всегда материализуете один элемент слишком много. С ровными потоками вы можете материализовать ровно столько элементов, сколько вам нужно. Дополнительную информацию см. В разделе Обоснование [SRFI 41] (http://srfi.schemers.org/srfi-41/srfi-41.html). –

+0

Да, я это видел, но все, что я видел, было основано на ошибочной чрезмерной попытке «взять». (cf [this] (http://stackoverflow.com/questions/38778261/how-to-use-lazy-evaluation-in-guile-scheme/38778442?noredirect=1#comment64969430_38778351)). Это очень легко кодировать правильно, то это возражение уходит. Одна вещь - это потоки, в которых каждый элемент вынужден самостоятельно; другой - это то, что получение элемента автоматически заставляет все предыдущие; оба имеют свое место; Я предполагаю, что последнее должно быть гораздо более распространенным, с правильным «взятием», конечно. –