2010-05-24 85 views
9

假設我有以下類型簽名的函數:使用項目

g :: a -> a -> a -> b 

我也有a列表S-姑且稱之爲xs - 即我知道將至少包含三個項目。我想將g應用於xs的前三項。我知道我可以定義如下的組合器:

($$$) :: (a -> a -> a -> b) -> [a] -> b 
f $$$ (x:y:z:_) = f x y z 

然後我可以使用g $$$ xs。這使得$$$有點像uncurry,但是對於具有相同類型的三個參數和列表而不是元組的函數。

有沒有一種方法可以使用標準組合器來通俗地做到這一點?或者說,在Haskell中最常用的方法是什麼?我認爲在非中綴版本$$$上嘗試使用pointfree可能會讓我對從何處開始有所瞭解,但輸出結果是可憎的,有10 flip s,幾個head s和tail s和ap s和28個括號。 (注:我知道這並不是一個非常糟糕的事情,但我遇到過幾種似乎是合理解決方案的情況,特別是在使用Parsec時。當然接受「不永遠這樣做是真正的代碼」,如果這是最好的答案,但我更願意看到一些聰明的把戲涉及((->) r)單子或什麼的。)

+4

我沒有看到你的代碼有什麼問題。它很短並且重要。並非所有事情都必須是(或應該是)無點。 – sepp2k 2010-05-24 18:33:22

+1

我不一定認爲它有什麼問題 - 我只是很好奇是否有一個簡潔的方法來做到這一點,而不需要定義像'$$$'這樣的新組合器。 – 2010-05-24 18:35:46

+0

請注意,這是一個部分函數,​​所以...可能是一個糟糕的主意一般:)你可能會更好用一個'($$$)::(a - > a - > a - > b) - > [a] - >也許b' – 2014-08-03 00:49:44

回答

12

或者說,什麼是最地道的方式在Haskell做到這一點?

地道?如果你真的想要一個能做($$$)的函數,你的代碼可能就像你會得到的那樣習慣。

我更願意看到一些聰明的把戲

哦,在情況。

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
class ListApply f a r | f -> a r where 
    ($...) :: f -> [a] -> r 

instance (TypeCast b r) => ListApply b a r where 
    x $... _ = typeCast x 

instance (ListApply f a r) => ListApply (a -> f) a r where 
    f $... (x:xs) = (f x) $... xs 

你去那裏,一個完全通用的解決方案:任意給定元數的函數簽名像a -> a ... -> b,它適用於需要一個列表[a]的許多元素。演示:

ones :: [Int] 
ones = repeat 1 

test1 x = x 
test2 x y = x + y 
test3 x y z = (x + z) * (y + z) 

在GHCI:

> test1 $... ones 
1 
> test2 $... ones 
2 
> test3 $... ones 
4 

我一定會接受 「永遠不要做這在真正的代碼」,如果這是最好的答案

你可能想要去那個。


哦,有點樣板的運行上面的代碼需要:

class TypeCast a b | a -> b, b->a where typeCast :: a -> b 
class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b 
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b 
instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast'() x 
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast'' 
instance TypeCast''() a a where typeCast'' _ x = x 

這類型級元編程的Oleg Kiselyov禮貌的瑞士軍刀。

7
f $$$ (x:y:z:_) = f x y z 

在我看來這是最習慣和簡潔的方式。如果參數的數量是不同的,你可以使用模板哈斯克爾或做迭代 - 定義:

zero = const 
next n f (x:xs) = n (f x) xs 

那麼你的功能next (next (next zero))),而這個工程的next任何嵌套。

你也可以打破它更原始的組合程序:

firstThree (x:y:z:_) = (x,y,z) 
uncurry3 f (x,y,z) = f x y z 
g f = uncurry3 f . firstThree