Я пробираюсь через "ANSI Common Lisp" Пола Грэма (1996).
Глава 3, упражнения, кв. 2 просит функцию, как указано в заголовке этого поста. Я использую только то, чему научили в книге до этого момента (очевидно, существует конструкция case
, которая может очистить if
, но я не против этого в настоящее время).
В качестве первой попытки я написал чередование, в котором сохранились дубликаты:
(defun interleave (x y)
(if (and (null x)
(null y))
nil
(if (null x)
(cons (car y)
(interleave (cdr y) x))
; where y is null, but also for any other case:
(cons (car x)
(interleave y (cdr x))))))
После этого у меня появилась идея сохранить перенос элементов, которые были замечены, и перейти к вспомогательной функции, как показано ниже.
Тем не менее, ниже, очевидно, довольно некрасиво и трудно понять.
Я ищу несколько предложений о направлениях, которые я мог бы принять, чтобы достичь элегантности
Советы по подходу и стилю могут быть столь же полезными на данном этапе, как и предоставление канонического решения. Должен ли мой импульс номер один из приведенного ниже кода извлекать другую функцию? (или, может быть, я пошел в неправильном направлении, пытаясь сперва сохранить керри?) Спасибо вам, хакеры!
(defun new-union (x y)
(new-union-helper x y '())) ; <- idea, add a carry to store what's been seen.
(defun new-union-helper (x y seen)
(if (and (null x)
(null y))
nil
(if (null x)
(if (not (member (car y) seen)) ; if first el of y hasn't yet been seen...
; cons it to the ultimate result & recur, while adding it to seen:
(cons (car y) (new-union-helper (cdr y) x (cons (car y) seen)))
; if it has been seen, just continue, (skip the duplicate):
(new-union-helper (cdr y) x seen))
(if (not (member (car x) seen))
(cons (car x) (new-union-helper y (cdr x) (cons (car x) seen)))
(new-union-helper (cdr x) y seen)))))
Обновление: я попытался заменить вложенные if
s на cond
, просмотрев cond
в указателе книги. Извините заранее, это так некрасиво ... но если кто-нибудь скажет мне, что я здесь делаю не так, я был бы очень признателен. Этот код работает так же, как и выше, но он печатает ноль в качестве последнего члена в результирующем списке (на некоторых входах), пока не уверен почему.
; attempt to use cond instead:
(defun new-union-helper (x y seen)
(cond ((and (null x) (null y))
nil)
((and (null x) (not (member (car y) seen)))
(cons (car y) (new-union-helper (cdr y) x (cons (car y) seen))))
((null x)
(new-union-helper (cdr y) x seen))
((not (member (car x) seen))
(cons (car x) (new-union-helper y (cdr x) (cons (car x) seen))))
(t
(new-union-helper (cdr x) y seen))))
Обновление 2: я попытался улучшить отступ. Ниже приведено то, что я хочу сделать из неформальных тестов. Любые дальнейшие советы о том, что я все еще делаю неправильно? (Я понимаю, что, возможно, мне следует отказаться от этого и пойти другим путем, но, поскольку это учебное упражнение, я хотел вылечить как можно больше потенциальных вредных привычек, прежде чем продолжать идти по новому пути).
Как эта ставка на уродство делает ставку? :) Это теперь доступно для чтения опытному лисперу?
; better (standard?) formatting
(defun new-union-helper (x y seen)
(cond ((and (null x)
(null y))
nil)
((and (null x)
(member (car y) seen)) ; replacing find with member stops duplicate nils
(new-union-helper (cdr y) x seen))
((null x)
(cons (car y)
(new-union-helper (cdr y) x
(cons (car y) seen))))
((member (car x) seen)
(new-union-helper (cdr x) y seen))
(t
(cons (car x)
(new-union-helper y (cdr x)
(cons (car x) seen))))))