2011-06-28 9 views
9

Mam ten pomysł od XKCD's Hofstadter comic; jaki jest najlepszy sposób utworzenia pętli warunkowej w (dowolnym) dialekcie Lisp, który wykonuje funkcję, dopóki nie zwróci NIL, kiedy zbierze zwrócone wartości do listy.Wykonuje funkcję, aż zwróci zero, zbierając swoje wartości do listy.

Dla tych, którzy nie widzieli tego dowcipu, to prawda, że ​​"ośmiokrotna" autobiografia Douglasa Hofstadtera składa się tylko z sześciu słów: "Jestem tak meta, nawet ten akronim", co stanowi kontynuację żartu: (niektóre dziwny meta-paraprosdokian?) "Is Meta" - żart jest taki, że autobiografia jest w rzeczywistości "Jestem taki meta, nawet ten akronim jest meta". Ale dlaczego nie pójść głębiej?

przejąć funkcję acronymizing META który tworzy skrót z ciągiem i dzieli go na słowach zwraca NIL jeśli ciąg zawiera tylko jednym słowem:

(meta "I'm So Meta, Even This Acronym") ⇒ "Is Meta" 
(meta (meta "I'm So Meta, Even This Acronym")) ⇒ "Im" 
(meta (meta (meta "I'm So Meta, Even This Acronym"))) ⇒ NIL 

(meta "GNU is Not UNIX") ⇒ "GNU" 
(meta (meta "GNU is Not UNIX")) ⇒ NIL 

Teraz szukam sposobu realizacji funkcji tak:

(so-function #'meta "I'm So Meta, Even This Acronym") 
⇒ ("I'm So Meta, Even This Acronym" "Is Meta" "Im") 
(so-function #'meta "GNU is Not Unix") 
⇒ ("GNU is Not Unix" "GNU") 

Jaki jest najlepszy sposób na zrobienie tego?

+1

Odpowiedź Jordana "oszukuje" przez podłączenie tylko do określonych przypadków. W ogólnym celu (meta), musisz zdecydować, gdzie są granice słów. Możesz użyć pliku słownika. – compman

+0

@compman, jego pytanie mówi się konkretnie o założeniu istnienia 'meta', więc nie nazwałbym tego" oszustwem ". Nadal masz rację. Nie jestem do końca pewien * jak * zdecydować, gdzie są granice słów, nawet biorąc pod uwagę słownik. Jednym szczególnym problemem byłby sposób, w jaki zdecydowałbyś się na jedno słowo i powinien on zostać rozwiązany. EDYCJA: Jednym ze sposobów może być powiedzenie "jeśli to słowo znajduje się w moim słowniku lub jeśli nie mogę podzielić tego słowa na dowolną liczbę pod-słów, tak, że wszystkie znajdują się w słowniku, zakończ". –

Odpowiedz

3

To jest łatwe. Nie chcę napisać rozwiązanie, więc zamiast tego będzie - ale to będzie brzydko wersję Elisp, co może prowadzić do nieoczekiwanych oświecenia, jeśli będziesz postępować przez:

(defun so-function (f str) 
    (let (x '()) 
    (while str (setq x (cons str x)) (setq str (funcall f str))) 
    (reverse x))) 

Aby to wypróbować trzeba, że ​​meta, ale nie wiem, jak chcesz zdecydować, gdzie umieścić przestrzenie, więc zamiast tego będę udawać:

(defun meta (x) 
    (cadr (assoc x '(("I'm So Meta, Even This Acronym" "Is Meta") 
        ("Is Meta" "Im") 
        ("GNU is Not UNIX" "GNU"))))) 

to sprawia, że ​​kod, który chcesz pracę. Co do oświecenia - spróbuj napisać więc zamiast tego, co chcesz, so-function będzie funkcja wyższego rzędu - taki, który będzie działał tak:

(funcall (so-function #'meta) "GNU is Not UNIX") 

lub w schemacie:

((so-function meta) "GNU is Not UNIX") 

Najważniejszą wskazówką jest to, że nie można tego zrobić w prostym elicie (przynajmniej nie bez sztuczek z biblioteki cl). Aby uzyskać pełny kredyt, unikaj mutacji - doprowadzi to do naturalnego sposobu zapisania go w Scheme, a nawet może wyglądać bardziej czytelnie niż wersja setq.

+0

Czy twoja funkcja Emacs Lisp version version nie powinna być '(funcall # ', tak-function #' meta" GNUS nie jest UNIX ")', która zwraca '(" GNU is not UNIX "" GNU ")'? (i podobnie '(funcall # 'tak-function #' meta" Jestem So Meta, nawet tym akronimem ")' ⇒ '(" Jestem taki meta, nawet ten akronim "" Jest meta "" Im ")') –

+0

Albo po prostu '(tak # funkcja # 'meta autobiografia)' jak w odpowiedzi Jordana Wade'a –

+0

Nie - patrz początek tej części, gdzie powiedziałem "spróbuj to napisać tak ..." - to jest ćwiczenie wydał! –

1

Threw tym razem i wydaje się działać:

(defun collect-until-null (function initial-value) 
    "Collects INITIAL-VALUE and the results of repeatedly applying FUNCTION to 
    INITIAL-VALUE into a list. When the result is NIL, iteration stops." 
    (if initial-value 
     (cons initial-value 
      (collect-until-null function (funcall function initial-value))))) 

Korzystanie z nieco zmodyfikowaną wersję meta Eli Barzilay pisał,

(defun meta (x) 
    (cadr (assoc x 
       '(("I'm So Meta, Even This Acronym" "Is Meta") 
       ("Is Meta" "Im") 
       ("GNU is Not UNIX" "GNU")) 
       :test #'equal))) ;strings with the same characters aren't EQL 

uzyskać wynik, którego szukasz.

CL-USER> (collect-until-null #'meta "I'm So Meta, Even This Acronym") 
("I'm So Meta, Even This Acronym" "Is Meta" "Im") 

CL-USER> (collect-until-null #'meta "GNU is Not UNIX") 
("GNU is Not UNIX" "GNU") 

Edit: @Rainer Joswig wskazał, że collect-until-null wyczerpie stos jeśli podano wystarczająco dużą sekwencję. Poniżej znajduje się wersja iteracyjna Rainera bez tego problemu.

(defun collect-until-null-iter (function initial-value) 
    "Collects INITIAL-VALUE and the results of repeatedly applying FUNCTION to 
    INITIAL-VALUE into a list. When the result is NIL, iteration stops." 
    (loop for result = initial-value then (funcall function result) 
     while result collect result)) 
+0

przepełnienie stosu ... –

+0

@Rainer Joswig, czy mógłbyś być bardziej konkretny? Ponownie uruchomiłem go w świeżej instancji SBCL i nie miałem żadnych problemów. –

+0

po prostu użyj długiej listy –

Powiązane problemy