생각해 볼 질문
몇 가지 프레임 변환만으로 재사용할 수 있는 그림 조합 언어를 어떻게 만들까요? 하위 프레임을 만들어 페인터 변환하기 beside와 below를 프로시저 조합기로 정의하기 같은 변환 원시 연산으로 뒤집기와 회전 표현하기 기본 페인터를 바꾸지 않고 네 부분 합성 만들기 깊이에 따라 선분 수가 자라는 재귀 분할 만들기 렌더링한 SVG를 선택한 실행의 선분 transcript와 연결하기 transform-painter는 페인터의 선분 데이터를 다시 쓰지 않습니다. 단위 정사각형의 제어점 세 개를 바깥 프레임으로 옮기고 새 하위 프레임을 만듭니다. 원래 페인터는 그 하위 프레임 안에서 실행됩니다. beside와 below는 변환된 페인터 두 개를 만들고 선분 목록을 이어 붙이며 뒤집기와 회전도 제어점 세 개를 다르게 고른 결과입니다.
첫 실행은 변환한 갈매기 모양 네 개를 한 정사각형에 놓습니다. 두 번째는 right-split을 재귀적으로 정의합니다. 왼쪽 절반에는 원래 페인터를 두고 오른쪽 절반에는 작은 복사본 두 개를 위아래로 쌓습니다. 깊이 3에서 선분 세 개짜리 기본 페인터는 옮긴 선분 45개를 만듭니다. 시각화는 리스펙스가 출력한 SEG 줄만 그리며 브라우저 렌더링 제한은 런타임 자체의 실행 한도와 구분됩니다.
SICP 코드 UTF-8 4,491 / 1,048,576바이트
( begin
( define ( make-vect x y ) ( list x y ) )
( define ( xcor-vect vector ) ( car vector ) )
( define ( ycor-vect vector ) ( cadr vector ) )
( define ( add-vect left right )
( make-vect ( + ( xcor-vect left ) ( xcor-vect right ) )
( + ( ycor-vect left ) ( ycor-vect right ) ) ) )
( define ( sub-vect left right )
( make-vect ( - ( xcor-vect left ) ( xcor-vect right ) )
( - ( ycor-vect left ) ( ycor-vect right ) ) ) )
( define ( scale-vect factor vector )
( make-vect ( * factor ( xcor-vect vector ) )
( * factor ( ycor-vect vector ) ) ) )
( define ( make-segment start end ) ( list start end ) )
( define ( start-segment segment ) ( car segment ) )
( define ( end-segment segment ) ( cadr segment ) )
( define ( make-frame origin edge1 edge2 )
( list origin edge1 edge2 ) )
( define ( origin-frame frame ) ( car frame ) )
( define ( edge1-frame frame ) ( cadr frame ) )
( define ( edge2-frame frame ) ( caddr frame ) )
( define ( frame-coordinate-map frame )
( lambda ( vector )
( add-vect
( origin-frame frame )
( add-vect
( scale-vect ( xcor-vect vector ) ( edge1-frame frame ) )
( scale-vect ( ycor-vect vector ) ( edge2-frame frame ) ) ) ) ) )
( define ( map-segment coordinate-map segment )
( make-segment
( coordinate-map ( start-segment segment ) )
( coordinate-map ( end-segment segment ) ) ) )
( define ( segments->painter segment-list )
( lambda ( frame )
( let ( ( coordinate-map ( frame-coordinate-map frame ) ) )
( map ( lambda ( segment )
( map-segment coordinate-map segment ) )
segment-list ) ) ) )
( define ( transform-painter painter origin corner1 corner2 )
( lambda ( frame )
( let* ( ( coordinate-map ( frame-coordinate-map frame ) )
( new-origin ( coordinate-map origin ) )
( mapped-corner1 ( coordinate-map corner1 ) )
( mapped-corner2 ( coordinate-map corner2 ) ) )
( painter
( make-frame
new-origin
( sub-vect mapped-corner1 new-origin )
( sub-vect mapped-corner2 new-origin ) ) ) ) ) )
( define ( beside left right )
( let ( ( paint-left
( transform-painter
left
( make-vect 0.0 0.0 )
( make-vect 0.5 0.0 )
( make-vect 0.0 1.0 ) ) )
( paint-right
( transform-painter
right
( make-vect 0.5 0.0 )
( make-vect 1.0 0.0 )
( make-vect 0.5 1.0 ) ) ) )
( lambda ( frame )
( append ( paint-left frame )
( paint-right frame ) ) ) ) )
( define ( below bottom top )
( let ( ( paint-bottom
( transform-painter
bottom
( make-vect 0.0 0.0 )
( make-vect 1.0 0.0 )
( make-vect 0.0 0.5 ) ) )
( paint-top
( transform-painter
top
( make-vect 0.0 0.5 )
( make-vect 1.0 0.5 )
( make-vect 0.0 1.0 ) ) ) )
( lambda ( frame )
( append ( paint-bottom frame )
( paint-top frame ) ) ) ) )
( define ( flip-horiz painter )
( transform-painter
painter
( make-vect 1.0 0.0 )
( make-vect 0.0 0.0 )
( make-vect 1.0 1.0 ) ) )
( define ( flip-vert painter )
( transform-painter
painter
( make-vect 0.0 1.0 )
( make-vect 1.0 1.0 )
( make-vect 0.0 0.0 ) ) )
( define ( rotate180 painter )
( transform-painter
painter
( make-vect 1.0 1.0 )
( make-vect 0.0 1.0 )
( make-vect 1.0 0.0 ) ) )
( define ( emit-segment segment )
( let ( ( start ( start-segment segment ) )
( end ( end-segment segment ) ) )
( display "SEG " )
( display ( xcor-vect start ) )
( display " " )
( display ( ycor-vect start ) )
( display " " )
( display ( xcor-vect end ) )
( display " " )
( display ( ycor-vect end ) )
( newline ) ) )
( define ( paint painter frame )
( let ( ( segments ( painter frame ) ) )
( for-each emit-segment segments )
( list ' segments ( length segments ) ) ) )
( define chevron
( segments->painter
( list
( make-segment ( make-vect 0.1 0.12 )
( make-vect 0.5 0.9 ) )
( make-segment ( make-vect 0.5 0.9 )
( make-vect 0.9 0.12 ) )
( make-segment ( make-vect 0.25 0.42 )
( make-vect 0.75 0.42 ) ) ) ) )
( define quartet
( below
( beside chevron ( flip-horiz chevron ) )
( beside ( flip-vert chevron ) ( rotate180 chevron ) ) ) )
( paint quartet
( make-frame ( make-vect 0.04 0.04 )
( make-vect 0.92 0.0 )
( make-vect 0.0 0.92 ) ) )
) 코드 실행Ctrl/⌘ Enter 파일 열기 코드 저장 코드 복사 편집기 지우기
예제 변환한 페인터 네 개 합성하기 재귀 right-split 그리기
실행은 브라우저 안에서 이루어지며 프로그램 결과와 실행 추적을 보여줍니다. 변환한 페인터 네 개 합성하기 재귀 right-split 그리기 실행 및 렌더링
이 실행에서는 유효한 SEG 라인이 생성되지 않았습니다.
프로그램 출력에서 0개의 선분이 렌더링되었습니다.
예제 변환한 페인터 네 개 합성하기
렌더링된 선분 0
형식이 잘못된 SEG 라인 0
프로그램 값 —
추적 상태 —
런타임 아티팩트 —선분 목록 SVG는 브라우저 실행에서 출력된 SEG 라인을 렌더링합니다. 예상 결과 네 부분 합성은 SEG 줄 열두 개를 출력하고 (segments 12)를 반환합니다. 깊이 3 right-split은 SEG 줄 45개를 출력하고 (segments 45)를 반환합니다. 두 SVG 모두 선택한 리스펙스 transcript에서 만들어집니다.
실행 추적에서 볼 점 기본 페인터가 선분을 옮기기 전에 transform-painter가 하위 프레임을 만드는 각 호출을 찾으세요. right-split에서는 재귀 깊이를 따라 반환되는 페인터 프로시저를 본 뒤 프로시저 합성과 paint 시점의 유한 선분 생산을 구분하세요.
직접 해보기 프로그램을 수정하고 결과를 비교해 보세요. 원래 페인터를 아래에 두고 작은 복사본 두 개를 옆으로 놓아 그 위에 쌓는 up-split을 정의하세요. 깊이 3을 그리기 전에 선분 수를 예상하세요.
힌트 보기 below painter (beside smaller smaller)를 사용하세요. 양의 깊이마다 현재 복사본 하나와 작은 복사본 두 개를 만들므로 선분 수 점화식은 right-split과 같습니다.