project-euler 1 using factor

18 views
Skip to first unread message

jcm...@gmail.com

unread,
Feb 15, 2009, 3:38:31 AM2/15/09
to factor-kr
처음으로 작성한 factor code 네요..
막 작성을 시작할때는 막막했는데 문제를 하나씩 해결해 나가다 보니 완성이 되었네요 ^^

: sum-multipler ( x -- y ) dup 3 mod zero? [ + ] [ dup 5 mod zero?
[ + ] [ . ] if ] if ;
: project-euler1 ( -- ) 0 1000 >array [ sum-multipler ] each ;
project-euler1

제 생각에는 작성한 코드가 stack effect declaration 하고 definition 하고 맞지 않는거 같은데 어떻
게 생각하시나요..

코드 리뷰를 얼마든지 환영합니다.

이번일을 계기로 실패를 두려워 해서는 안되겠다는 생각이 듭니다.^^

Jonghyouk, Yun

unread,
Feb 15, 2009, 4:57:42 AM2/15/09
to factor-kr
저는 코드를 이렇게 바꿔봤어요.

<code>
USING: arrays kernel math prettyprint sequences ;
IN: euler-jcm-001


: mod-by-3? ( n -- ? ) 3 mod zero? ;

: mod-by-5? ( n -- ? ) 5 mod zero? ;

: project-euler1 ( -- n )
1000 [ [ mod-by-3? ] [ mod-by-5? ] bi or ] filter sum ;
<code>

실행은
"project-euler1 ." 이렇게요.


stack-effect-declaration이 맞지 않는 부분은 sum-multipler랑 project-euler가 둘 다 였
던거 같애요.

stack-effect은 좌변에 해당 word가 소비할 스택의 갯수랑 우변은 생산하는 스택의 갯수인데,
가까운예로 project-euler1은 실제로 스택에 여분값(불필요한)을 하나 남기고, sum-multipler도 그럴것 같애
요.

제가 생각하고 바꾼 부분은 다음과 같아요.

오... 저도 zero? 같은 워드가 있는줄은 몰랐는걸요.

1. 중첩된 if문 대신에 bi 콤비네이터를 사용한것. 하나의 값이 "3의 배수거나 5의 배수"인 조건을 만족하는데
중첩된 if을 쓰거나, or로 줄줄이 엮는것도 좋겠지만 팩터스럽게 하는게 좋을것 같아서 이렇게 했습니다.
그냥 or을 이용해도 되겠지만, 그럴때는 불필요하게 dup같은걸 사용해야할것 같아서요.

코드는
[ mod-by-3? ] [ mod-by-5? ] bi or
이 부분인데요 스택의 하나의 값을 mod-by-3?이랑 mod-by-5?에 알아서 복사해서 적용하고 그 두개의 결과를 or로 묶
었습니다.

bi, tri, bi@, bi*등의 cleave combinator을 이용하면 스택조작으로 복잡한것을 명확하게 표현할수있죠.

2. 0 1000 >array 부분은 사실 의미가 달라서 1000으로 바꿨습니다.
팩터에서는 정수도 sequences로 이용이 가능합니다. 예를 들어 "3 [ sq ] map"의 결과는 { 0 1 4 }죠.
(sq은 제곱, 2 sq은 4겠죠.)
즉, 다시 말해서 "0 1000 >array" 이렇게 하시면 실제로 원하시는대로 1000까지의 수열을 구하기도 하지만
그 배열값 바로 위쪽에 '0'이 덜렁 혼자 스택에 쌓여있죠. (그리고 stack effect을 틀리게 만들겠죠?)

추가적으로 어떤 수의 범위에 대해서 이런 수열 시퀀스를 만드시려면 math.ranges의 [a,b]라던지 (a,b),
[1,b) 이런 워드들을 살펴보시면 좋을듯합니다.
(수학에서 끝값 포함/비포함처럼 (a,b), [a,b]이런 표기법이죠?)
math.ranges은 실제로 메모리를 차지하지 않고, range을 표현하기 좋은 방법이죠. (파이썬이나 루비의 그것처럼.)

3. each을 대신에 filter로 바꿨습니다. (filter, each, times, map, reduce...등의
functional형제들!)
1000 [ even? ] filter 이렇게 한다면 1000이하의 짝수만 걸러내서 sequence로 돌려주죠.

4. 그리고 간단히 수열의 합을 구하라는 문제였으니까 sum 함수를 이용해서 합을 구했습니다.
+워드와 reduce을 조합해서 만들어도 되겠지만 이미 있는 워드가 있으니까 이용했습니다.

5. 개발을 하실때 어떤 환경을 사용하시는지 잘 모르지만, ui listener라면 :errors라고 프롬프트에서 치시면 관련
에러를 살펴보실수있답니다. :-)


저도 처음 팩터로 작성할때는 정말 어색하고 이상했는데 점점 익숙해지고 그러니 다른 언어로는 작성하고 싶지 않아지네요.(재밌어서
요!)


그럼 해피 팩터링!

Jonghyouk, Yun

unread,
Feb 15, 2009, 5:04:59 AM2/15/09
to factor-kr
추가적으로 mod-by-3?이나 이런걸 mod-by-n?으로 바꿀수도 있겠죠

: mod-by? ( n divisor -- ? ) mod zero? ;

...

[ 3 mod-by? ] [ 5 mod-by? ] bi or

가능하면 코드가 중복되지않게, 간결하고 명확한 표현을 하도록 하는게 팩터코드의 묘미인것 같습니다.

C언어를 공부할때도 포인터를 잘 활용하고 하는게 중요하지만, 괴상하게 연산자의 우선순위에 따른 괴상한 문제에 집착하는게 바람직하
지 않듯이 stack-shuffler을 잘 이용하는게 중요하겠지만 그게 바람직한거라고는 생각하기 힘들겠죠.

스택영향이 괴상하고 이해하기 어렵다면,
로직이 스택연산을 복잡하게 만드는건 아닌지 자문해보고
a) 로직을 분해해서 간단히 만들거나
b) 아예 로직을 바꾸거나
c) 정 아니라면 locals등의 지역변수을 이용하거나
...이런정도가 아닐까 생각합니다.

jcm...@gmail.com

unread,
Feb 17, 2009, 5:53:32 PM2/17/09
to factor-kr
답변이 좀 늦었습니다..ㅎ
코드가 이렇게 깔끔해질수 있다니 다시 한번 팩터의 힘의 놀라네요.
정성스런 가이드에 감사합니다.

Jong-Hyouk Yun

unread,
Feb 17, 2009, 9:23:52 PM2/17/09
to fact...@googlegroups.com
아니에요. 늦기는요^^

간결하고 생각 표현하는 도구에서 그치지 않고, 생각을 확장하는 도구로서 좋은 도구라고 생각합니다. :-)

좋은하루되세요!

2009년 2월 18일 (수) 오전 7:53, jcm...@gmail.com <jcm...@gmail.com>님의 말:

Reply all
Reply to author
Forward
0 new messages