2017-09-29 1 views
2

파이썬 방식으로 소수를 생성하려고합니다. 즉, 생성자를 사용하고 있습니다.Common Lisp에서 느리게 생성 프라임을 생성

파이썬 코드는 더 많거나 적은 내가 리스프에 아주 새로운 해요

def divisor_in_list(n, d): 
    """ Returns true if a divisor of n is in d, false otherwise" 

def primes(): 
    yield 2 
    primelist = [2] 
    i = 3 

    while True: 
     while divisor_in_list(i, primelist): 
      i += 2 
     primelist.append(i) 
     yield i 

다음, 그래서 관용적 상응하는 것이 무엇인지 궁금 할 것이다. 이 시점에 내 연구의 최대 바탕으로, 나는 그것이 밖으로 뱉어 첫 번째 값이 나는하지 못하고, 불행하게도 3 점이다이 코드에 문제가있다,

(defun primes() 
    (let* ((p (list 2)) (n 3)) 
    (lambda() 
     (loop while (divisor-in-slist n p) 
      do (incf n 2)) 
     (nconc p (list n)) 
     (+ n 0) ;; Not actually sure how to return N directly :(
    ) 
    ) 
) 

그러나 다음과 같은 코드가 있습니다 그것을 우아하게 수정하여 첫 번째 값으로 2를 생성하는 방법을 알아 내야합니다.

람다에있는 if 문과 여분의 변수를 결합하여 메서드가 처음으로 호출되는지 여부를 확인할 수 있지만 추한 것처럼 보일 수 있습니다. 더 좋은 방법은 무엇입니까?

+0

참고로 "게으른"키워드 => 게으른 라이브러리 : [clazy] (https://common-lisp.net/project/clazy/)의 – Ehvince

+0

가능한 중복이 있습니까 파이썬의 생성기와 똑같은 간단한 lisp?] (https://stackoverflow.com/questions/32956033/is-there-a-straightforward-lisp-equivalent-of-pythons-generators) – coredump

+0

추악한 사람은 보는 사람의 눈에 들어 있습니다. 파이썬 솔루션에서는 하드 코드 된 초기 수율 2, 알려진 코드의 하드 코딩 된 초기화 [2], 3에서 반복, 마지막으로 2 씩 증가하는 "치트"를 파이썬 솔루션에서 많이 볼 수 있습니다. 소수는 "2"라고 말하지 않고 3 등 간격으로 건너 뛰는 것으로 시작합니다. " 그래서 파이썬 솔루션은 구워진 소수 (primes)에 대해 우리가 알고있는 것에 대해 모든 종류의 속임수를 가지고 있습니다. "1보다 큰 정수, 두 개의 정수 요소가 1과 그 자체 만 있습니다"에서 다시 시도하십시오. 그럼 당신은 단지 하나의 못생긴 해킹이 필요합니다 :> 2로 건너 뜁니다. – kennytilton

답변

6

Common Lisp에는 yield의 직접 해당 항목이 없습니다. 기능적 접근법을 사용하거나 게으른 계산을 제공하는 일종의 라이브러리를 사용할 수도 있습니다.

접근 방법을 완료하는 한 가지 방법은 현재 연속을 유지하는 변수 f이있는이 방법과 같습니다.

(defun make-prime-generator (&aux (p (list 2)) (n 2) f) 
    (labels ((f2()   ; a function for the first iteration 
      (incf n) 
      (setf f #'fn) ; setting f to the next function 
      2) 
      (fn()   ; a function for all other iterations 
      (loop while (divisor-in-list n p) 
        do (incf n 2)) 
      (push n p) 
      n)) 
    (setf f #'f2)   ; setting f to the first function 
    (lambda()    ; returning a closure 
     (funcall f))))   ; which calls the current f 


CL-USER 28 > (let ((p (make-prime-generator))) 
       (flet ((p() (funcall p))) 
       (loop repeat 10 do (print (p))))) 

2 
3 
5 
7 
11 
13 
17 
19 
23 
29 
NIL 

야심 찬 사람이라면, 모든 코드 부분을 정의하고 전환을 관리하는 매크로 뒤에 숨어있을 수 있습니다.

또한 탐사

우리는 상태를 만들 수는 좀 더 명시 적으로 로컬 기능 init, exitstep을 도입하여 변경합니다.

(defun make-prime-generator (&aux (p (list 2)) (n 2) f) 
    (flet ((init (function) 
      (setf f function)) 
     (exit (result function) 
      (setf f function) 
      result) 
     (step() 
      (funcall f))) 
    (labels ((f2() 
       (incf n) 
       (exit 2 #'fn)) 
      (fn() 
       (loop while (divisor-in-list n p) 
        do (incf n 2)) 
       (push n p) 
       (exit n #'fn))) 
     (init #'f2) 
     #'step))) 

이제이 다른 것, 조금 더 고급 작업 : 상용구를 제거하고 코드를보다 선언적 만들기 위해 우리를 수있는 매크로 gen-run 물품. 그것은 다음과 같이 사용할 수 있습니다

(defmacro gen-run (f-descriptions &key start) 
    (let ((§f (gensym "F")) 
     (§init (gensym "INIT")) 
     (§exit (gensym "EXIT")) 
     (§step (gensym "STEP"))) 
    `(let (,§f) 
     (flet ((,§init (function) 
       (setf ,§f function)) 
       (,§exit (result function) 
       (setf ,§f function) 
       result) 
       (,§step() 
       (funcall ,§f))) 
     (labels (,@(loop for fd in f-descriptions 
         collect (destructuring-bind (name -> next &body body) 
            fd 
           (declare (ignore ->)) 
           `(,name() 
            (,§exit ((lambda() ,@body)) 
              (function ,(if next next name))))))) 
      (,§init (function ,start)) 
      (function ,§step)))))) 

(defun make-prime-generator (&aux (p (list 2)) (n 2)) 
    (gen-run ((f2 -> fn 
       (incf n) 
       2) 
      (fn -> fn 
       (loop while (divisor-in-list n p) 
        do (incf n 2)) 
       (push n p) 
       n)) 
     :start f2)) 
관련 문제