最近更新: 2007-01-13

動態語言關於參數宣告的寫作風格

tokimeki 日前回應的文章中提到 然後在函數內作過濾參數動作 ,讓我想起在不同程式語言對參數宣告一事有著不同的寫作風格。我就從參數宣告的寫作風格中,展現一下不同程式語言的各種風貌吧。

從 BASIC, C, Pascal 所延襲的傳統參數宣告形式要求我們定義函數時須將參數一一列出。到 C++ 採用 overloading 概念後,雖然讓我們不用再為函數編號 (func1, func2, func3, etc.) ,但我們還是要定義多個同名函數。但在動態語言中,我們有不同的思考與寫作風格。

BASIC,C, Pascal 的傳統寫作風格要求在定義函數時,須一一列出參數及其型別。如 example.cpp 所示。

example.cpp
void myFunction(int a) {
}

void myFunction(int a, char*b) {
}

void myFunction(int a, char*b, string c) {
}

int main(int argc, char**argv) {
    myFunction(1, "Hello");
    return 1;
}

這種傳統寫作風格在動態語言中有所改變。由於動態語言強大的動態性往往使其無法按參數宣告的型別賦于同名函數不同簽名,故執行環境無法依訊息型別決定該調用哪一個同名函數。一一列出參數的傳統宣告方式,反而又為我們帶來編號函數名稱的困擾。再者,動態語言基於動態定義的功能需求,實務上遇到函數同名的狀況時,會以最後定義的函數內容取代先前定義的函數內容,而非增加一個複載。由於動態語言的強大動態性,我們傾向將要傳遞給函數的訊息內容包裹成一個參數,於是我們不需要在參數宣告中將參數一一列出。如 example.js 所示。

example.js
function myFunction(args) {
    if (args['id'] == undefined) {
        throw 'id is required';
    }
    if (args['value'] == undefined) {
        value = 0;
    }
    if (args['src']) {
    }
    if (args['alt']) {
    }
}

myFunction({id: "xyz", src: "xx.jpg", alt: "X", value: 0});

包裹在一起的作法也有缺點。一、我們從函數宣告中看不出參數的意義。二、在函數定義中增加了許多判斷某一訊息是否有被包裹在參數中的程式碼,亦即多了許多 if (args[x]) {} 的動作。因此在實務上,我們往往採用折衷作法,列出必要的參數,而不必要的參數包裹起來。如 example.php 所示。

example.php
function myFunction($id, $args, $value = 0) {
    if (isset($args['src'])) {
    }
    if (isset($args['alt'])) {
    }
}

myFuncion('xyz', array('src'=>'xx.jpg', 'alt'=>'X'));

比較 example.js 與 example.php ,我們不難發現列出參數宣告的作法可以省下不必要的函數定義內容。例如在 example.php 中不必檢查必要訊息 id ,也不必檢查訊息 value 是否需要採用預設值。

我在本文中使用了三種程式語言,這是因為各種程式語言各有其特性而帶來不同的寫作風格,像 example.js 就不能套用 example.php 的寫法。多了解這些特性將幫助我們寫出更精練的程式碼。

相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/2642460.html

樂多舊回應
HACGIS@gmail.com(tokimeki) (#comment-3842558)
Sat, 13 Jan 2007 19:58:29 +0800
很同意你的看法。
本來同一種技巧就不見得適用每一種程式語言,不過我上次說的「在函數內作過濾參數動作」,其實有一句但書沒講,那就是我個人在設計函數時,習慣用關聯陣列來傳遞參數,而且通常會假設每個參數都有預設值。

因為在PHP中可以傳遞參照的關係,所以我把這個動作寫成一個函數:

function 陣列:過濾(&$陣列, &$預設 = array()){
return array_intersect_key($陣列 + $預設, $預設);
}

所以我只要在函數內設定一個預設陣列,然後把參數陣列以及預設陣列丟進去處理就行了,傳回來就得到過濾好的參數陣列,而且保證每個參數都有值。

接下來就可以對每個參數作驗證、運算等動作。