2017-01-31 2 views
1

Brainf ** k를 Common Lisp, SBCL로 구현하려고했습니다. 나는 몇 가지 문제를 겪었다.Brainf ** k가 Common Lisp에서 구현되었습니다.

(defparameter *tape* (make-array '(1) :adjustable t)) 
(defparameter *pointer* 0) 
(defparameter *tape-size* 1) 
(defparameter *output* (make-array '(0) :element-type 'base-char :fill-pointer 0 :adjustable t)) 

(defun move-pointer-right (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(progn 
    (incf *tape-size*) 
    (adjust-array *tape* (list *tape-size*)) 
    (incf *pointer*))) 
(defun move-pointer-left (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(progn (decf *pointer*))) 
(defun increment-byte (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(incf (aref *tape* *pointer*))) 
(defun decrement-byte (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(decf (aref *tape* *pointer*))) 
(defun start-loop (stream ch) 
(declare (ignore ch)) 
(let ((loop-body (read-delimited-list #\] stream t))) 
`(loop :until (zerop (aref *tape* *pointer*)) 
     :do ,@loop-body))) 
(defun print-one-char (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(with-output-to-string (s *output*) (write-char (code-char (aref *tape* *pointer*)) s))) 
(defun read-one-char (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(setf (aref *tape* *pointer*) (char-code (read-char *standard-input*)))) 
(defun flush-output (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(progn *output*)) 
(defun reset-me (a b) 
(declare (ignore a)) 
(declare (ignore b)) 
'(progn 
    (setf *output* (make-array '(0) :element-type 'base-char :fill-pointer 0 :adjustable t)) 
    (adjust-array *tape* '(1)) 
    (setf (aref *tape* 0) 0) 
    (setf *pointer* 0))) 
(set-macro-character #\< #'move-pointer-left) 
(set-macro-character #\> #'move-pointer-right) 
(set-macro-character #\+ #'increment-byte) 
(set-macro-character #\[ #'start-loop) 
(set-macro-character #\= #'flush-output) 
(set-macro-character #\. #'print-one-char) 
(set-macro-character #\, #'read-one-char) 
(set-macro-character #\! #'reset-me) 
(set-macro-character #\- #'decrement-byte) 
  • 입력 내가 중첩 루프 때문에 일하는 것이 있는지 확실하지 않다
  • 작동하지 않습니다 "["를 읽고에 "]"당신이하려고하면 "[/ 명령 [/ 더]/반신 반의] "나는이 방법으로 얼마나/모호한 것이 될지 짐작할 수 없다.
  • "++ [-> +> + < <]"시도했습니다. 내가 아는 한 배열에는 "0 2 2"가 있어야하지만 대신 "0 2 0"이 있습니다. 나는 뭔가 잘못되었다고 결론을 내린다.
  • 나는 SBCL로부터 많은 경고를 받고있다. 그럴 필요가 없다 :/
  • "move-pointer-right"와 같은 함수에서 리턴 된 모든 생성 된 코드를 빠르게 출력 할 수 있는가? 파일로?
  • 출력은 "="명령에서 인쇄 할 하나의 문자열로 저장됩니다. 나는 다른 작업들이 많은 쓸모없는 것을 표준 출력에 인쇄했기 때문에 그것을했다. 나에게 큰 문제는 아니며,이 해결 방법 대신 단순히 파일로 인쇄하는 것을 상상하기 쉽습니다.
  • 영어로 실수를해서 유감입니다.

편집 : 코드를 편집했습니다 (다시 도움 주셔서 감사합니다, Sylwester). 입력을 제외한 모든 것이 작동하는 것 같습니다.

  • 입력에 관해서는 : 나는 read-char을 사용하지만, 내가 원하는 방식으로 작동하지 않습니다. 예를 들어 ,D은 "D"를 입력합니다. 다시 실행하여 각 ,에서 평가를 중단하고 사용자 입력을 기다립니다.

  • 질문 : 값을 반환하지 않습니다 progn에 대한 대안은 (난 그냥하지만 ​​반환하지 평가하려는)이 있습니까? 예를 들어 (what-i-look-for (setf a 1) 1 2)1a을 설정하지만, 그래서 *earmuffs*으로 양호하게는, 당신은 당신이 전역 변수로 tape, pointeroutput를 정의 할 필요가 작동하는데 어떻게 생각하는지에 대해 너무 많이 알고하지 않고 2

답변

1

을 반환하지 않습니다 당신은 그것들이 전역이라고 볼 수 있습니다.

(defparameter *tape* (make-array '(1) :adjustable t)) 

은 그 때 나는 >는 기본 요소 nil*tape*을 확장났습니다. 그러므로 모든 >에 대해 사실이 아니라면 0으로 설정해야합니다 (모든 값은 nil을 제외한 모든 값이 참입니다). pointer은 항상 테이프의 끝에 있다고 생각되는 것 같습니다. >>>++++<<<을 수행 할 때 4이 포함 된 요소는 오래 전에 사라졌습니다.

loop-body은 전역 변수입니다. 패키지 레벨 변수를 clobber하지 않으려면 여기 let을 사용해야합니다. loop을 잘못 사용했습니다. Loop for black belts의 예를 참조하십시오. 예 :커먼 리스프를 알려줍니다

(defun start-loop (stream ch) 
(declare (ignore ch)) 
(let ((loop-body (read-delimited-list #\] stream t))) 
`(loop :until (zerop (aref *tape* *pointer*)) 
     :do ,@loop-body))) 

공지 거기 declare는 사용하지 ch 무시합니다. read-deliited-liststart-loop이고 새 [이되므로 중첩은 자동으로 수행됩니다.

print-one-char은 ascii 값을 기준으로 char을 추가하지 않지만 숫자로 추가합니다. 또한 일반적으로 BF에서 즉시 인쇄하는 것이 일반적이므로 print-char이 더 좋을 수도 있습니다. =을 누를 때까지 메모리에 계속 유지하려면 문자열 입력 스트림에 인쇄 할 수 있습니다.

read은 lisp 데이터를 읽습니다. 따라서 a 대신 #\a을 입력해야합니다. 대신 read-char을 사용하십시오.

나는이 시점에서 충분할 것으로 생각합니다. 매크로 및 독자 매크로를 사용하면 멋지게 보이지만 판독기 매크로를 추가 한 후에는 해당 문자로 구성된 코드에 문제가 있으므로 디버깅 및 확장하기가 어렵습니다. [을 제외한 각 연산에 ​​대해 하나의 함수를 만드는 것은 테스트 할 수 있으므로 테스트를 단순화 할 것이고 매크로는 호출로 확장 될 것입니다.

(defun move-pointer-left() 
    (assert (> *pointer* 0) (*pointer*) "Tape pointer out of bounds: ~a" *pointer*) 
    (decf *pointer*)) 

(set-macro-character #\< (constantly '(move-pointer-left))) 
+0

대단히 감사합니다. 나는 질문을 편집했다. 입력을 제외한 모든 것이 이제는 작동하는 것 같습니다. 입력에 관해서 :'d '는'100'을 입력합니다 (이것은 # \ d에 대한 ASCII 코드입니다). ** 사용자 입력을 기다린 다음 평가를 계속 ** 평가를 중단하고 싶습니다. 이 작업을 수행하는 쉬운 방법이 있습니까? –

+0

@ PrzemysławP 그건 그렇습니다. 예. '(defun test(),.,.,.)'는 입출력을하기 위해'test'를 실행할 때까지 기다립니다. 그러나', .'을 REPL에 쓰면 점을 읽거나 더 많은 행을 읽고 입력을 폴링 할 가능성이 있습니다. – Sylwester

+0

현재 버전'(defun test(),.)'에서는 컴파일되지 않습니다. "목록에 아무것도 표시되지 않습니다." 나는'read-one-char'을 재정의하거나 지금은 입력을 포기하고 받아들이지 않을 것입니다. 어쨌든 다시 한번 감사드립니다. –

관련 문제