2016-07-07 49 views
1

我寫了一個函數來評估給定數量的多項式。多項式被表示爲係數列表(例如,[1,2,3]對應於x^2+2x+3)。擺脫不必要的括號

polyEval x p = sum (zipWith (*) (iterate (*x) 1) (reverse p)) 

正如您所看到的,我首先使用了大量的括號來對應對哪些表達式進行評估。爲了更好的可讀性,我嘗試使用.$消除儘可能多的括號。 (在我看來,兩對以上的嵌套括號使得代碼越來越難以閱讀。)我知道函數應用程序具有最高的優先級並且保持關聯。該.$都是右結合的,但.優先9,而$優先0

所以,在我看來,下面的表達式不能用更少括號寫

polyEval x p = sum $ zipWith (*) (iterate (*x) 1) $ reverse p 

我知道我們需要括號(*)(*x)將它們轉換爲前綴函數,但有可能以某種方式刪除圍繞iterate (*x) 1的括號嗎?

另外你還喜歡哪種版本的可讀性?

我知道還有很多其他的方法可以達到同樣的效果,但我想討論一下我的特殊例子,因爲它有一個函數在兩個參數(iterate (*x) 1)中作爲另一個帶有三個參數的函數的中間參數。

+4

爲了便於閱讀,我更喜歡摺疊。順便說一句,這可以完成點:'polyEval'x = foldl'(\ b a - > a + x * b)0'。 – Alec

+0

@Alec我剛剛更新了我的問題:我構造了這個特殊的例子,因爲你有一個函數在兩個參數中作爲另一個參數進行評估,並且希望討論這個特定的例子。 (順便說一句:你的函數並不是完全沒有點的自由,因爲你明確地使用lambda的參數) – flawr

+1

假設一個代碼結構保持原樣,我認爲'... where powers = iterate(* x)1'會幫幫我。我會使用'polyEval x = sum。 zipWith(*)權力。 reverse' – chi

回答

1

所以它可能是有點幼稚,但我其實真的很喜歡在食物方面想Haskell的規則。我認爲Haskell的左結合功能應用f x y = (f x) y作爲一種侵略性NOM貪婪NOM的,在該函數f拒絕等待y恢復過來,並立即吃f,除非你花時間把這些東西放在括號內,形成一種「論證三明治」f (x y)(在這一點上,x,未被吃掉,變得飢餓,並吃y)。唯一的界限是運營商和特殊形式。

然後在特殊形式的邊界內,操作員會消耗他們周圍的任何東西;最後,特殊形式花時間消化他們周圍的表情。這是.$能夠保存一些括號的唯一原因。

最後這一點,我們可以看到,iterate (* x) 1很可能將需要在三明治因爲我們不想要的東西只是吃iterate和停止。因此,除非我們能夠以某種方式取消第三個參數zipWith - 但該參數包含p,因此需要編寫一些內容更加無點,否則沒有很好的方法可以在不更改代碼的情況下做到這一點。

所以,一個解決方案是改變你的方法!將多項式存儲爲已反轉方向上的係數列表會更有意義,因此您的示例存儲爲[3, 2, 1]。那麼我們不需要執行這個複雜的reverse操作。這也使得數學更簡單一些,因爲兩個多項式的產品可以遞歸改寫爲(a + x * P(x)) * (b + x * Q(x))這給簡單的算法:

newtype Poly f = Poly [f] deriving (Eq, Show) 

instance Num f => Num (Poly f) where 
    fromInteger n = Poly [fromInteger n] 
    negate (Poly ps) = Poly (map negate ps) 
    Poly f + Poly g = Poly $ summing f g where 
     summing [] g = g 
     summing f [] = f 
     summing (x:xs) (y:ys) = (x + y) : summing xs ys 
    Poly (x : xs) * Poly (y : ys) = prefix (x*y) (y_p + x_q) + r where 
     y_p = Poly $ map (y *) xs 
     x_q = Poly $ map (x *) ys 
     prefix n (Poly m) = Poly (n : m) 
     r = prefix 0 . prefix 0 $ Poly xs * Poly ys 

然後你的函數

evaluatePoly :: Num f => Poly f -> f -> f 
evaluatePoly (Poly p) x = eval p where 
    eval = (sum .) . zipWith (*) $ iterate (x *) 1 

缺乏周圍iterate括號因爲eval是以無點式編寫的,所以$可用於消耗表達式的其餘部分。正如你所看到的,不幸的是,在(sum .)左右會出現一些新的括號,但這樣做可能並不完全值得。我覺得後者的可讀性比,也就是說,

evaluatePoly (Poly coeffs) x = sum $ zipWith (*) powersOfX coeffs where 
    powersOfX = iterate (x *) 1 

我甚至可能更喜歡寫後者,如果在高功率性能不超臨界,作爲powersOfX = [x^n | n <- [0..]]powersOfX = map (x^) [0..],但我認爲iterate不是太難在一般情況下理解。

2

像往常一樣,我更喜歡OP的版本到目前爲止已經提出的任何替代方案。我會寫

polyEval x p = sum $ zipWith (*) (iterate (* x) 1) (reverse p) 

並將其留在那。 zipWith (*)這兩個參數以與*這兩個參數相同的方式播放對稱角色,因此eta-reducing只是混淆。

$的值是它使得計算的最外層結構變得清晰:一個點上的多項式的評估是某物的總和。消除括號本身不應該是一個目標。

0

也許將其分解爲更多基本功能將進一步簡化。首先定義一個點積函數來乘以兩個數組(內積)。

dot x y = sum $ zipWith (*) x y 

和更改polyEval術語的次序,以最小化括號

polyEval x p = dot (reverse p) $ iterate (* x) 1 

減少到3對括號的。