โจทย์ ruby #21 ด้วย Erlang

  • warning: realpath() [function.realpath]: SAFE MODE Restriction in effect. The script whose uid is 1005 is not allowed to access /tmp owned by uid 0 in /var/www/sites/sugree/codenone.com/subdomains/www/html/includes/file.inc on line 190.
  • warning: realpath() [function.realpath]: SAFE MODE Restriction in effect. The script whose uid is 1005 is not allowed to access /tmp owned by uid 0 in /var/www/sites/sugree/codenone.com/subdomains/www/html/includes/file.inc on line 190.

ตามมาจากโจทย์ใน forum ruby
โจทย์ ruby #21

ถ้าจะใช้ erlang เขียน มันจะลำบากขึ้นมาหน่อย
เพราะมีประเด็นที่มันต่างจาก imperative คือ
1. ตัวแปร assign ได้ครั้งเดียว ห้ามแก้ไข (immutable)
2. พวก list ก็เป็น immutable เหมือนกัน
ดังนั้น ประโยคแบบนี้ a = []; a << 1
ไม่มีใช้ใน erlang
3. ไม่มี global variable ให้ใช้

solution แรกสุด ใช้ pattern matching กับ tail-recursive

-module(part).
-export([part/2]).
 
part(L,Size) ->
    part(L, [], Size).
 
part([], X, _) ->
    lists:reverse(X);
part([H|T], [], Size) ->
    part(T, [[H]], Size);
part([H|T], [H2|T2], Size) when length(H2) == Size ->
    part(T, [[H]] ++ [H2|T2], Size);
part([H|T], [H2|T2], Size) ->
    part(T, [H2 ++ [H]] ++ T2, Size).

++ คือการ append array เข้าด้วยกัน
Note: การที่เราใช้ recursive มันมักจะจบลงด้วยการ reverse ค่าผลลัพท์สุดท้าย

แบบที่ 2 ปรับปรุงมาจากตัวแรก
เพิ่มให้มีตัวพัก เพื่อให้ดูง่ายขึ้น และไม่ต้องทำ reverse ด้วย

part2(L,Size) ->
    part2(L, Size, [], []).
 
part2([], _, Temp, Result) ->
    Result ++ [Temp];
part2([H|T], Size, [], Result) ->
    part2(T, Size, [H], Result);
part2([H|T], Size, Temp, Result) when length(Temp) == Size ->
    part2(T, Size, [H], Result ++ [Temp]); 
part2([H|T], Size, Temp, Result) ->
    part2(T, Size, Temp ++ [H], Result).

แบบที่ 3 ลองหันไปใช้ higher order function บ้าง

part3([X|XS], Size) ->
    lists:reverse(
      lists:foldl(fun(Elm, [H|T]) ->
			  if length(H) == Size ->
				  [[Elm]] ++ [H|T];
			     true ->
				  [H ++ [Elm]] ++ T
			  end
		  end,
		  [[X]],
		  XS)).
mk's picture

GeShi ไม่สนับสนุน Erlang ซะด้วย หาใน issue tracker ของ sourceforge ก็ไม่เห็น สงสัยจะได้ทำเองแล้วมั้ง

GeShi document section #4

sugree's picture

mk: สงสัย… สมเป็นภาษานอกกระแสของนอกกระแส

อืม แบบที่สามแนวคิดคล้ายๆ ของอ.มะนาว

veer's picture

ลองเขียนเป็น ocaml มาใส่บ้าง พยายามแปล part3 (จริงๆผมคงมั่วออกนอกลู่นอกทางจาก part3 มาพอสมควร) น่าจะเป็นเพราะผมยังไม่แม่น ocaml แล้วก็มั่วเรื่อง functional ด้วย ออกมาแล้ว เละๆ ชอบกล T_T

open List;;
let last x = (hd (rev x));;
let cn21 a n =
    let rec f8 x b =
        if (length (last x)) > (n - 1) then
            f8 (append x [[]]) b
        else
            append (tl (rev x)) [(append (last x) [b])]
    in
    fold_left f8 [[]] a
;;
let m = [1;2;3;4;5;6;7;8;9;10];;
cn21 m 2;;

last x นี่มันดูโหดๆอยู่นะ (ในแง่ที่ต้อง reverse list ทุกครั้ง)

ดูไปสักพัก rev เต็มไปหมดเลย :)

veer's picture

ดูๆแล้วเหมือน list ใน caml ดูมันจำกัดหลายอย่าง (อยากใช้ a[s..e] จะแย่) ใช้ Array แทนละกัน ไม่ใช้ rev ละ แฮ่ๆ

let last x = x.((Array.length x) - 1);;
 
let cn21 a n =
    let rec sep x b =
        if Array.length (last x)  > n - 1 then
            sep (Array.append x [|[||]|]) b
        else 
            Array.append 
                (Array.sub x 0 ((Array.length x) - 1))
                [|(Array.append (last x) [|b|])|]
    in
    Array.fold_left sep [|[||]|] a
;;
 
let m = [|1;2;3;4;5;6;7;8;9;10|];;
cn21 m 2;;

ใน caml list ประกาศโดย [1;2;3] พอเป็น Array ก็ใช้ [|1;2;3|] เป็นเครื่องหมายที่น่าทึ่งจริงๆ -_-'

ดูยากจัง...
ไม่ต้อง fold_left ได้มะ?

อ้อ ดูผิด พยายามเขียนให้เป็น part 3 นี่นา

veer's picture

เดี๋ยวผมพยายามเขียนแบบอื่นดูมั่งครับ ผมเลือกแบบ fold_left ก่อนเพราะมันไปเทียบกับ list comprehension ของ Python ได้ :-P

veer's picture

หลังจากที่เล่น ocaml แล้วใช้ inspector ไม่เป็น ก็เลยพยายามไปหา IDE ดีๆ มาใช้ :-P
ก็ได้ DrScheme มา ประกอบยังติดใจเรื่องการใช้ List อยู่ ก็เลยต้องเปลี่ยนมาเขียน scheme ด้วย -_-'

ผมว่าไม่ได้เหมาะกับมือใหม่ functional programming นะ
แต่มันเหมือนกับการเขียนโปรแกรมครั้งแรกเลยทีเดียว !!!
(ถ้าไม่นับ Logo อะนะ)

แล้วก็มี code มาอีก version ใช้ reverse น้อยกว่า ocaml #1 หน่อย

;; For codenone-ruby #21
(define (cn21 l n)
  (local ((define (sep b ans)
            (cond 
              [(empty? ans) (cons (list b) ans)]
              [(= (length (first ans)) n) (cons (list b) ans)]
              [else (cons (cons b (first ans)) (rest ans)) ])))
    (reverse (map reverse (foldl sep empty l)))))
 
(cn21 (list 1 2 3 4 5 6 7 8 9 10) 3)

อ๊ะ ผมก็ใช้ drscheme อยู่นะ
แต่ไม่ยักรู้ว่าใช้เขียน ocaml ได้ด้วย
ขอบคุณครับ จะได้ลองเอา code ของวีร์ไปลอง run มั่ง

อ่านผิด ใจร้อนไปหน่อย

veer's picture

ของ ocaml มี camelia อะครับ แต่ก็ดูห่างชั้นจาก DrScheme

ที่บางมหาวิทยาลัย สอน Scheme เป็นภาษาแรกเลยอ่ะ

veer's picture

ประถมเรียน Logo เข้ามหาลัยเขียน Scheme ก็น่าจะดี แต่แหมมันไม่ real world lol

ลองเป็น scheme ด้วย
แต่ใช้วิธีเดียวกับ haskell หรือ ML ใน post โจทย์ ruby #21

(define (sep lst n)
  (define (split-at lst cnt buff)
    (if (or (= cnt 0) (null? lst))
        (list buff lst)
        (split-at (cdr lst) (- cnt 1) (append buff (list (car lst))))))
  (let ((tmp (split-at lst n '())))         
    (if (null? (cadr tmp))
        (list (car tmp))
        (append (list (car tmp)) (sep (cadr tmp) n)))))
(sep '(1 2 3 4 5 6 7 8 9) 2)
;; => ((1 2) (3 4) (5 6) (7 8) (9))

ย้าย Codenone

ประกาศย้าย Codenone ไปใช้ Forum ของ Blognone แทนครับ ตามไปตั้งกระทู้ต่อได้ที่ Codenone Forum (รายละเอียดอ่านจากกระทู้ ย้าย Codenone ไปรวมกับ Blognone)

กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น