最近更新: 2009-12-09

遞迴與Smalltalk式介詞應用

前陣子用 Ruby 寫一個文件區塊的格式化工具,它會先載入整個文件區塊到陣列,然後依序將每個段落交給不同的方法處理。 被調用的方法,會從陣列中取出它要處理的段落,直到段落結束。 因為處理過的段落內容已經從陣列中取出,所以下一個方法總是從陣列的開頭繼續處理。

我先用 Ruby 寫幾個小程式來演練概念,寫著寫著就玩起來了。 一共寫了三種作法,分別應用了不同的 Ruby 語法。

  1. 平行指派,後綴式迴圈、無遞迴
  2. 遞迴式
  3. 遞迴式,Smalltalk 式介詞應用,加上迭代器

平行指派,後綴式迴圈、無遞迴

a = [1, 2, 3]
v = []

def take x
  #yield
  return x.shift, x
end

i = -1  #這是一個小瑕玼

v[i+=1], a = take a while a.size > 0
  #上行讀作: 當 a 還有東西時,就拿出一個放到 v 中。
p a
p v

i = -1
a[i+=1], v = take v until v.size < 1
  #上行讀作: 從 v 拿出一個東西放到 a 中,直到 v 裡面沒有東西。
p a
p v

沒有用到遞迴,記憶體需求比遞迴式少。 不過遞迴式的寫法也不難,一併完成吧。

遞迴式

a = [1, 2, 3]
v = []

#take([H|T]) -> H, take(T);
#take([]) -> [].

def take x, y
  if x.size > 0
    y << x.shift
    take x, y
  end
  return x
end

take a, v
p a
p v

Smalltalk 式介詞應用,加上迭代器

編程大師總是說 Smalltalk 的表達力非常優秀。而 Ruby 的教科書上也常常說 Ruby 的語法可以提供像 Smalltalk 的表達能力,適合開發領域特定語言(DSL)。 那我就用 Ruby 來玩一下類似 Smalltalk 在訊息中使用介詞的表達能力吧。

# take(V, [H|T]) -> take(V, T);
# take(_, []) -> [].

a = [1, 2, 3]
v = []

def take x, preposition, y, &p
  if preposition == :from
    take y, :to, x, &p
  else
    if x.size > 0
      y << x.shift
      yield y.last if block_given?
      take x, preposition, y, &p
    end
  end
  return x
end

take a, :to, v
#英文是我們的第一外國語言,應該不用解釋上面那一句話了吧?
# a -> v

p a
p v

#介詞變成 from ,所以方向反過來了。
# a <- v
take a, :from, v
p a
p v

puts '印出每次取得的東西'
take( a, :to, v ) {|n|
  puts "Take #{n}"
}
p a
p v

Smalltalk 可寫成:

take a to: v.
take a from: v.

take: x to: y
  "your code"
  ^x

take: x from: y
  "your code"
  ^y

文如其名,近乎日常用語的抽象表達力。 而且 Smalltalk 可以分成兩個訊息(方法)來實作, 不像上頭的 Ruby 用 if 分開流程。程式結構更清晰。

此外,當我在寫遞迴時,我腦中不由得想到 Erlang 的作法。每次從清單中取出頭部處理,尾部作為另一個清單交給下一個方法處理。

method([H|T]) -> H, method(T);
method([]) -> [].

在我學 Erlang 之前,遞迴程式的設計藍圖從未如此清晰地浮現在我眼前。儘管我對 Erlang 的認識還很粗淺,但是它的序列編程風格,已經成功影嚮我改進遞迴設計。

我學過的程式語言不算多,屈指可數。在這之中, Java 語言對我的影嚮最低,趨近於零;事實上我總是儘可能地忘掉 Java 語言的風格,理由不言可知。

每一種程式語言都有許多獨特的語法,構成形形色色的表達力。儘管我們認為其中一些語法只不過是甜頭、糖衣。但語言的表達能力不可思議地影嚮我們的軟體開發生產力。表達能力愈是接近日常口語或日常符號系統的,它的生產力就愈高。

雖然 Smalltalk 等語言並不流行,而且我寫著不同的語言,但那並不妨礙我學習他們的優點。儘可能地嘗試引用他們的概念,強化程式碼的表達能力,提昇生產力。再者,程式碼即文件,程式的表達能力愈貼近日常用語,我就愈不需要維護多份設計文件。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/10972865.html

樂多舊回應
aa@bb.cc(b4283) (#comment-21378741)
Fri, 05 Nov 2010 21:20:28 +0800
模擬 erlang 可以使用星號指派
x, *y = [1,2,3,4]
x = 1
y = [2,3,4]