Добавление элемента в список на схеме

Ниже приведен мой код, который принимает в качестве параметров элемент car списка list(carVal) и список (инициализированный пустым). Я хочу добавить элемент в список, но то же самое не работает.

(define populateValues
   (lambda (carVal currVal)
      (append currVal(list carVal ))
       (display currVal)))

На дисплее все время отображается пустой список () . Может ли кто-нибудь помочь мне понять, почему?


person name_masked    schedule 03.07.2010    source источник


Ответы (5)


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

Кроме того, set! не изменяет данные, а изменяет окружение, делает переменную ссылкой на другую вещь, исходные данные остаются неизменными.

Изменение данных в Scheme довольно громоздко, но, чтобы дать вам мою собственную реализацию append! посмотреть, как это делается:

(define (append! lst . lsts)
  (if (not (null? lsts))
      (if (null? (cdr lst))
          (begin
            (set-cdr! lst (car lsts))
            (apply append! (car lsts) (cdr lsts)))

          (apply append! (cdr lst) lsts))))

Обратите внимание на использование set-cdr!, который является настоящим мутатором, он работает только с парами, он изменяет данные в памяти, в отличие от `set!'. Если пара передана функции и изменена с помощью set-cdr! или set-car!, он мутирует везде в программе.

Это подчиняется добавлению SRFI! spec, в котором говорится, что он должен быть вариативным и, например, должен возвращать неопределенное значение.

(define l1 (list 1 2 3 4))

(define l2 (list 2 3 4))

(define l3 (list 3 1))

(append! l1 l2 l3)

l1

l2

l3

Что отображает:

(1 2 3 4 2 3 4 3 1)
(2 3 4 3 1)
(3 1)

Как видно, дописываю! может принимать бесконечное количество аргументов и изменяет их все, кроме последнего.

Тем не менее, Scheme может быть не идеальным языком для вас. Использование добавления! как было сказано ранее, является нестандартным, вместо этого предпочтительнее добавление, которое не мутирует и вызывается для его возвращаемого значения. Который я реализую как таковой:

(define (append . lsts)
  (cond
    ((null? lsts) '())
    ((null? (car lsts)) (apply append (cdr lsts)))
    (else (cons (caar lsts) (apply append (cdar lsts) (cdr lsts))))))


> (append (list 1 2 3) (list 4 5 6) (list 'granny 'porn))
(1 2 3 4 5 6 granny porn)

Который показывает более знакомый стиль Scheme при отсутствии мутаций, интенсивного использования рекурсии и без использования секвенирования.

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

(define (extend l . xs)
  (if (null? l) 
      xs
      (cons (car l) (apply extend (cdr l) xs))))

(define (extend! l . xs)
  (if (null? (cdr l))
      (set-cdr! l xs)
      (apply extend! (cdr l) xs)))

(extend '(0 1 2 3) 4 5 6)

(define list1 '(0 1 2 3))

(extend! list1 4 5 6)

list1

Что делает то, что вы ожидаете

person Zorf    schedule 05.07.2010
comment
Спасибо за ответ .. Кстати .. granny , porn .. Возможно, вы захотите их изменить .. Иначе вы можете проиграть :) - person name_masked; 16.07.2010
comment
@ darkie15 Это не делает ответ менее «полезным» или «понятным», если люди хотят понизить его из-за этих вещей, то этот сайт уже потерян. Кроме того, у тебя все равно есть ответ. =) Кроме того, другие люди могут отредактировать его, если захотят. - person Zorf; 16.07.2010

  1. append создает новый список, а не изменяет существующий.
  2. Это потому, что в целом Scheme (и Racket в данном случае) — это язык, который предпочитает функциональный стиль.
  3. Вы могли бы приблизиться к set!, но даже это вас разочарует, так как оно изменит только локальную привязку.
  4. Обратите внимание, что в Racket, в частности, списки неизменяемы, поэтому ничто не может изменить список.
  5. Кроме того, даже если бы вы могли изменить список таким образом, это очень неэффективный способ накопления длинных списков, поскольку вам придется многократно просматривать весь список.
  6. Наконец, если у вас есть проблемы на этом уровне, я настоятельно рекомендую перейти на HtDP.
person Eli Barzilay    schedule 03.07.2010
comment
Мне нужно придумать эту функцию. Что бы вы предложили в этом случае? - person name_masked; 04.07.2010
comment
Вы можете использовать box, который является своего рода указателем на (изменяемое) значение. НО Я сомневаюсь, что вам действительно нужна эта функциональность — новички часто думают, что она у них должна быть, потому что они привыкли к мутации как к единственному способу что-то делать. - person Eli Barzilay; 04.07.2010

(append foo bar) возвращает конкатенацию foo и bar. Не меняется ни foo, ни bar.

person sepp2k    schedule 03.07.2010

Вы должны обновить значение currVal с помощью set!. Ваш пример должен иметь

(set! currVal (append currVal (list carVal))
(display currVal)
person keithm    schedule 03.07.2010
comment
Обратите внимание, что это изменит currVal внутри функции, но не будет иметь видимого эффекта снаружи. - person Eli Barzilay; 04.07.2010

Вам действительно нужно подумать о том, какую именно функциональность вы ищете

Если вы хотите изменить ссылочный список на месте, вам нужно сделать эквивалент добавления! (как отмечено в других ответах). Но это опасно, ПОТОМУ ЧТО у вас может быть другой код, который считается неизменяемым в списке, и если вы собираетесь это сделать, ваша процедура должна иметь ! в конце концов, чтобы отметить эту опасность.

Дешевое приближение к тому, что вы хотите сделать, в более функциональном стиле:

(define (populateValues carVal currVal)
 (let ((ll (append currVal (list carVal))))
   (display ll)
   ll))

Обратите внимание, что он создает новый список, выполняет добавление, отображает результат и ВОЗВРАЩАЕТ новый список в качестве значения. Это полезный метод отладки, если у вас нет доступа к промежуточному значению: привязать к переменной, отобразить или зарегистрировать ее, а затем вернуть.

person Dak    schedule 16.07.2010