在函式式程式設計中,什麼是累加器?

時間 2021-05-07 01:19:13

1樓:孫文全

這種東西就其用途來說稱之為累加器是不合適的,reducer規約器應該是更好的翻譯。

可以想象成有這麼個二元運算子Op,reduce的目的就是把源序列的元素相鄰兩個之間插入這麼個Op,得到乙個結果比如reduce([a;b;c;d], + ) => a + b + c + d

如果是簡單的算術累加我一般用sum之類的直接方法,reduce我最多的是用來處理字串……

2樓:

應該是累積器,並沒有 「加」 的意思。

乙個高階函式(引數可能是乙個函式的函式,可以想象成 C 中引數有函式指標的函式),名字可能叫 reduce fold accumulate。

簡單來說就是乙個函式 f,通過這個函式被傳進來的乙個引數函式 g,反覆地把乙個有順序的結構的下乙個值和累積結果作為引數呼叫:

f(g, (a, b, c)) = g(g(a, b), c)

以 Python 的 reduce 為例,引數是這樣的(手機答題不能參考,引數名不是正確的)

reduce(func, iter, init=None)

第乙個是乙個函式,這個函式可以接受兩個引數。

第二個是乙個迭代器(也就是可迭代的結構,比如說陣列,鍊錶這類有順序可以遍歷的結構)

第三個引數是可選的,表示累加器的初始值。

累加器具體實現依賴語言,這裡為了方便理解用過程式語言描述,實際上在 Lisp 中一般是尾遞迴實現,除了引數以外不建立變數也不修改變數。

檢查第三個引數 init 是否傳入,如果傳入那麼累加器的結果變數賦值: acc = init。如果沒有傳入,那麼將 iter 的第乙個值作為結果(如果是陣列的話就是 iter[0] )。

然後,獲取迭代器的下乙個值,並且應用 func(acc, next(iter)) 結果賦給 acc,不斷迴圈。

我這樣說一定不直觀,就看幾個例子好了,先來乙個函式

function add(int a, int b)

然後要寫陣列求和函式,直接

function sum(array)

再假設,要寫乙個鍊錶複製或者陣列轉化成鍊錶的函式:

function cons(value, next) // 構造鍊錶

function linked(iter)

等等等等,迴圈中結果累積在命令式(和物件導向)中被人寫過無數遍,為什麼不能抽取出來成為乙個函式呢?

這就是函式式的魅力,你可以把你的程式結構抽象出來,雖然要理解 reduce 這種累積器有學習成本,但是理解了以後發現這種強有力的統一抽象實在是簡潔直觀,比如說你可以把函式和資料組織在某種資料結構中,分分鐘實現物件導向。

3樓:

沒有全域性變數給你儲存狀態,只好多加個引數把這個變數帶著跑了。

accumulator在函式式程式設計裡是乙個common idiom。 因為有時候不用累加器寫出來的遞迴函式不是尾遞迴的,要消耗大量的棧空間。比如算從1加到100,

(define (sum x y)

(if (> x y) 0

(+ x (sum (+ x 1) y)))

這樣寫當然可以,但是問題是這樣要消耗很多棧空間,因為每個x都要儲存在棧上。

所以可以多加乙個變數, acc

(define (sum x y acc)

(if (> x y) acc

(sum (+1 x) y (+ acc x))))

看! acc 被帶著跑了,那麼原來棧上的東西就不要了,所以通過tail call optimization,棧就不會被額外消耗了。

這個東西翻譯過來叫累加器,其實就是一種儲存狀態的技巧,不一定都是用來累加的,最終目的是要用來TCO的。寫程式之前,想想每一步需要儲存哪些狀態,很容易就能寫出用累加器可以TCO的版本了。也因為如此,我覺得不帶TCO的語言都沒資格稱為支援Functional Programming.

4樓:baozii

我替一樓把程式寫好了

(define (foldl fun result lst)

(if (null? lst)

result

(foldl fun (fun result (car lst)) (cdr lst))))

(foldl (lambda (x y) (+ x y)) 0 '(1 2 3 4 5))

(foldl (lambda (x y) (* x y)) 1 '(1 2 3 4 5))

補充一下:累加器不是fold,是fold (+),fold是個更抽象的操作,比如fold (*) 就是累乘器

5樓:大魔頭-諾鐵

我估計樓主問的是foldl,foldr中的累加器吧比如sum可以用foldl實現

*Main> foldl (\acc x -> acc + x) 0 [1..10]

55這個acc被稱為累加器

foldl是個高階函式,它接受乙個函式f作為引數,然後傳兩個引數給這個函式f

乙個是累加器的初始值,上例中是0,另乙個是列表中的第乙個元素函式f對初始值和第乙個元素運算後產生新的累加器(acc+x),然後再取列表中的第二個元素再做同樣的運算,直到全部元素都計算完畢。

另外,上例也可以寫成更簡單的形式

*Main> foldl (+) 0 [1..10]55

6樓:王世友

因為純函式式語言是沒有狀態的,因此在一些迴圈計算的時候不能向過程式和OO語言那樣通過修改乙個變數來達到計算的目的,比如對乙個列表的數求和,python可以這樣:

sum = 0

for num in list:

sum += num

而函式式語言是不能這樣的,只能是使用遞迴,最簡單的形式是(haskell表示):

sum :: [Int] -> Int

sum (x:xs) = x + sum xs

sum [ ] = 0

其實,這裡用haskell來做例子是不合適的,因為haskell是lazy evaluation,我們假設是strict evaluation的,(對不起,今天喝了點酒,lisp一下子也不出來了)。這其實是乙個連加式,只有到了最後一項得到0後才能計算出結果。這是很耗記憶體的。

這時候就可以使用累加器了,累加器就是將當前的計算結果放到引數裡,也就是帶著當前的狀態了。

sum :: Int -> [Int] -> Int

sum a (x:xs) = sum (a+x) xs

sum a [ ] = a

這裡的a就是累加器。

因為用的是haskell,這裡的累加器不是乙個整數,而是乙個計算,只有到最後一步返回結果並且結果需要一定的計算的時候才會計算成乙個整數,否則結果就是乙個連加式的thunk,如果是lisp一類的strict evaluation語言,每一步的(a + x)都會計算,然後進入下乙個呼叫。即使如此,這兩個實現的區別還是很明顯的,第乙個是普通遞迴,第二個是尾遞迴。如果是lisp,第二個記憶體用量是不變的。

總的來說,累加器在strict evaluation的語言裡是更常見的。

函式式程式設計immutable data是不是本質上效能就差點?

個人認為是的,而且開發起來好像也更加麻煩,如果要更新值,那麼完了還要把原來的 replace 掉,甚至要把所有有關聯的地方都要做一次 update.意義是什麼?就為了乙個所謂的無隱患 copy 如果說的不對,還希望大家指出正確使用方式。 navegador 它不一定就得真正的去 malloc.只要在...

為什麼會有函式式程式設計?

Jason Hu 這個問題就是跟問 為什麼會有數學,數學是為了解決什麼問題 是乙個意思。基於lambda calculus的語言比基於TM的語言具有更數學含義。實際上,任一形式邏輯系統都對應一種函式式語言。相反,基於TM的語言自成一系,並無法繼承數學和邏輯系統裡的知識架構。另外,基於TM的程式語義也...

為什麼說函式式程式設計和命令式程式設計等價, 它們怎樣相互轉化

時空是一體 本體 的統一的,函式式 命令式是一體 本體 的統一的。圖1圖2 上面兩個圖是計算機裡的物件空間和物件的運動軌跡,這兩種有限集合圖可能是同乙個圖。被cpu執行緒驅動的主體物件沿著紅線行走,前乙個圖是主體觀察到的左手邊的世界,後乙個圖是主體觀察到的自己右手邊的世界。一致的世界,所有地方都一致...