(루프 …) 안에 백틱/쉼표 관용구를 사용하는 것이 맞나요?
문제
다음과 같은 루프에서 포인트(consed 정수)를 수집하는 코드가 있습니다.
(loop
for x from 1 to 100
for y from 100 downto 1
collect `(,x . ,y))
내 질문은 사용하는 것이 올바른지입니다. `(,x . ,y)
이러한 상황에서?
편집하다:이 샘플은 100x100 항목의 테이블을 생성하는 것에 관한 것이 아닙니다. 여기의 코드는 두 개의 루프 변수의 사용과 해당 값의 구성을 보여줍니다.이를 명확하게 하기 위해 루프를 편집했습니다.내가 사용하는 실제 루프는 여러 다른 함수에 따라 달라지며(그리고 함수 자체의 일부임) 호출을 리터럴 정수로 바꾸고 함수에서 루프를 꺼내는 것이 더 합리적입니다.
해결책
그냥 하는 것이 훨씬 '더 나을' 것입니다(단점 x y).
하지만 질문에 답하세요, 그렇게 하는 데 아무런 문제가 없습니다 :) (조금 느리게 만드는 것을 제외하고).
다른 팁
여기서 답은 자원 활용이라고 생각합니다. 이 게시물)
예를 들어 클립에서:
[1]> (time
(progn
(loop
for x from 1 to 100000
for y from 1 to 100000 do
collect (cons x y))
()))
WARNING: LOOP: missing forms after DO: permitted by CLtL2, forbidden by ANSI
CL.
Real time: 0.469 sec.
Run time: 0.468 sec.
Space: 1609084 Bytes
GC: 1, GC time: 0.015 sec.
NIL
[2]> (time
(progn
(loop
for x from 1 to 100000
for y from 1 to 100000 do
collect `(,x . ,y)) ;`
()))
WARNING: LOOP: missing forms after DO: permitted by CLtL2, forbidden by ANSI
CL.
Real time: 0.969 sec.
Run time: 0.969 sec.
Space: 10409084 Bytes
GC: 15, GC time: 0.172 sec.
NIL
[3]>
DSM:코드에 몇 가지 이상한 점이 있습니다 여기.참고하세요
(loop for x from 1 to 100000
for y from 1 to 100000 do
collect `(,x . ,y))
다음과 같습니다:
(loop for x from 1 to 100
collecting (cons x x))
아마도 당신이 의도한 것이 아닐 수도 있습니다.세 가지 사항에 유의하세요.첫째, 작성한 방식대로 x와 y는 동일한 역할을 합니다.아마도 루프를 중첩하려고 했을 것입니다.둘째, 뒤에 오는 lisp 형식이 없기 때문에 y 이후의 작업이 올바르지 않습니다.셋째, 여기에서 백틱 접근 방식을 사용할 수 있다는 점은 맞습니다. 그러나 코드를 읽기 어렵게 만들고 관용적이지 않으므로 피하는 것이 가장 좋습니다.
실제로 의도한 것이 무엇인지 추측하면 다음과 같은 작업을 수행할 수 있습니다(루프 사용).
(loop for x from 1 to 100 appending
(loop for y from 1 to 100 collecting (cons x y)))
Kyle과 같은 루프 매크로가 마음에 들지 않으면 다음과 같은 다른 반복 구성을 사용할 수 있습니다.
(let ((list nil))
(dotimes (n 100) ;; 0 based count, you will have to add 1 to get 1 .. 100
(dotimes (m 100)
(push (cons n m) list)))
(nreverse list))
이런 종류의 작업을 많이 수행하는 경우 목록 교차를 위한 보다 일반적인 함수를 작성한 다음 이 정수 목록을 전달해야 할 것입니다.
루프뿐만 아니라 반복에 문제가 있는 경우 이러한 종류의 작업을 재귀적으로 수행할 수 있습니다(그러나 이는 계획이 아니므로 구현 시 TCO가 보장되지 않을 수 있습니다).Kyle이 보여주는 함수 "genint" 여기 일반적인(표준은 아님) 기능 iota의 변형입니다.그러나 목록에 추가하는 것은 나쁜 생각입니다.다음과 같은 동등한 구현:
(defun iota (n &optional (start 0))
(let ((end (+ n start)))
(labels ((next (n)
(when (< n end)
(cons n (next (1+ n))))))
(next start))))
훨씬 더 효율적이어야 하지만 여전히 꼬리 호출은 아닙니다.참고 저는 이것을 보다 일반적인 0 기반으로 설정했지만 1이나 다른 정수에서 시작하는 선택적 매개 변수를 제공했습니다.물론 위의 내용은 다음과 같이 작성할 수 있습니다.
(defun iota (n &optional (start 0))
(loop repeat n
for i from start collecting i))
큰 논쟁으로 인해 스택을 날리지 않는다는 장점이 있습니다.구현이 마무리 호출 제거를 지원하는 경우 다음과 같이 수행하여 재귀가 제자리에서 벗어나는 것을 방지할 수도 있습니다.
(defun iota (n &optional (start 0))
(labels ((next (i list)
(if (>= i (+ n start))
nil
(next (1+ i) (cons i list)))))
(next start nil)))
도움이 되었기를 바랍니다!
왜 안되지?
(cons x y)
그런데 CLISP에서 코드를 실행하려고 했는데 예상대로 작동하지 않았습니다.나는 루프 매크로를 별로 좋아하지 않기 때문에 동일한 작업을 재귀적으로 수행하는 방법은 다음과 같습니다.
(defun genint (stop)
(if (= stop 1) '(1)
(append (genint (- stop 1)) (list stop))))
(defun genpairs (x y)
(let ((row (mapcar #'(lambda (y)
(cons x y))
(genint y))))
(if (= x 0) row
(append (genpairs (- x 1) y)
row))))
(genpairs 100 100)