Clojure中缺少多重返回值支持的原因是什麼? (Clojure似乎沒有任何類似於Common Lisp的東西values
/multiple-value-bind
)Clojure中爲什麼沒有多重返回值
顯式解構被認爲在功能性編程風格中被認爲更習慣,它與JVM有什麼關係,還是它被簡單地認爲是收益太少,太複雜了?
Clojure中缺少多重返回值支持的原因是什麼? (Clojure似乎沒有任何類似於Common Lisp的東西values
/multiple-value-bind
)Clojure中爲什麼沒有多重返回值
顯式解構被認爲在功能性編程風格中被認爲更習慣,它與JVM有什麼關係,還是它被簡單地認爲是收益太少,太複雜了?
這聽起來像你已經熟悉了基本的Clojure的解構與載體&地圖:
(defn calc-sqr-vec [x]
[ x (* x x) ]) ; returns a vector of 2 values
(defn calc-sqr-map [x]
{ :x x :y (* x x) }) ; returns a map of 2 entries
(let [ [x y] (calc-sqr-vec 3) ]
(println (format "3^2 -> [%d,%d]" x y)))
(let [ {:keys [x y]} (calc-sqr-map 3) ]
(println (format "3^2 -> [%d,%d]" x y)))
vec: 3^2 => [3,9]
map: 3^2 => [3,9]
,你包裹返回值在單矢量x
& y
或地圖,並且調用者在需要時拉出組件值。
我無法回答關於CL的why
部分,但與Python風格的多重返回值相比,一個很大的好處是在用戶沒有指定所有返回值時該怎麼做的問題。例如:
q, r = divmod(22, 7)
q => 3
r => 1
q = divmod(22, 7)
q => (3,1)
所以,在Python相同的表達divmod(22, 7)
生成不同結果依賴於該語句的「接收」部分。通過始終返回相同的單個值並允許調用者選擇何時提取所需位(並忽略不需要的位),可以避免此類複雜性。
更新
有趣的是,這個話題今天來,因爲就在昨天,我與需要回到一堆獨立值的函數工作。我寫了一個簡短的宏使其更容易。單元測試顯示它在行動:
(dotest
(let [some-fn (fn []
(let [a 1
b 2
c 3
d 4
e 5]
(label-value-map a b c d e))) ]
(is= {:a 1 :b 2 :c 3 :d 4 :e 5} (some-fn))
(let [ {:keys [a b c d e]} (some-fn) ]
(is= [a b c d e] [1 2 3 4 5]))))
因此,使用label-value-map
和純老{:keys [a b c d e]}
解構,你可以從一個地方到另一個地方用更少的打字轉移一堆標量值。 :)
我想答案爲什麼是因爲它沒有需要多重價值支撐,原因是不需要一個是因爲一個可以與宏很容易寫。我主要在Common Lisp中工作,並且瞭解一些Clojure,但似乎你可能是正確的,因爲它更具慣用性。 Clojure在哪裏有更多的支持地圖和矢量和解構他們比Common Lisp,所以它更容易,更安全,可能更有效的手動解構,而不是包括multiple-value-bind
和values
版本。
在一個側面說明,我很少用values
和multiple-value-bind
時,它使更多的慣用意義上的功能將「自然」在(mod 5 4)
,這裏只是有時我想第二個值的情況下返回一個值,如除。
宏和解構都不能替代多個值(我現在只是錯過了它們)。你甚至可以解釋爲什麼:調用代碼可以假裝其他值不存在,但特定的調用者可以查找它們,並且特定的調用可以選擇性地提供它們。最後是我所需要的,因爲只有一個調用者(但是大多數匿名函數只返回核心值)。因爲我只有一個調用者,所以我會用奇怪的lambda返回一個帶有合適meta的地圖,然後讓調用者解開它(減慢所有檢查!)。 – kennytilton
我們可以推測爲什麼,但是看看Clojure在這方面的功能,我認爲我們可以說它並不需要多個返回值。
開箱即用,在Clojure中有兩種模擬多重回報的基本方法:解構的複合值和動態綁定的輔助回報。
這裏是解構風格,所有的結果都急切地返回,並且可以根據需要被調用者解構:
(defn foo
"Returns a map with results at keys :ret and :aux."
[]
{:ret :primary, :aux :auxiliary})
(let [{:keys [ret]} (foo)]
(vector ret)) ; => [:primary]
(let [{:keys [ret aux]} (foo)]
(vector ret aux)) ; => [:primary :auxiliary]
這裏是動態無功返回輔助的風格,可選結果選入(呼叫者興趣的信號),並通過側通道返回:
根據風格,結果被計算並急切返回,或EXP由呼叫者合法地要求並因此按需求計算。這兩種風格都允許呼叫者選擇並選擇點菜結果。
我會添加到這個返回的元數據:https://clojure.org/reference/metadata –
*你*是一個野人!讓我想起C,在那裏我們可以選擇傳遞一個指向被調用函數可以寫輔助數據的位置的指針。動態變量當然是一種解決方法,拋開所有的荒謬。 – kennytilton
謝謝@DirkGeurs,我錯過了。現在我想到了,但我不明白元數據方法對複合值+解構方法會有什麼優勢 - 它只適用於支持「元」的返回值,比如地圖和矢量,所以適用性非常好狹窄,不是嗎? – glts
感謝您的詳細示例:)在我的情況下,我主要想知道爲什麼(基本原理)。如果函數沒有返回那麼多的值,那麼Common Lisp的'multiple-value-bind'只是將額外的變量綁定到nil。正如Clojure的解構一樣:'(let [[a b c] [1 2]] c)=> nil'。 Python並沒有真正的多元返回值 - 它只是具有語法糖,允許人們從元組中省略括號,因此它們看起來不像元組。 'divmod'總是返回一個包含兩個值的元組。 'q,r = <返回元組或列表的任何表達式>'是用於解包項目的糖。 – Lassi
與返回的數組和對象相比,多個返回實際上並沒有給你任何東西,只是它停留在堆棧上,並沒有分配堆內存。這只是一個優化。在方案I中,寧可不使用它們,因爲實施比CL更難使用。 – Sylwester
根據用戶是否爲所有返回值指定了綁定,我不會購買有關返回不同值的參數。 Python中的示例只是顯示'divmod'返回一個2元組,'x,y = f(a)'是解構它的語法,'x = f(a)'是綁定整個元組的語法。返回一個2向量的Clojure'divmod'看起來是一樣的,只是Clojure解構語法(當然)是不同的。 – amalloy