2015-12-17 72 views
12

我有一個Haskell任務(不,這不是我的作業,我正在學習考試)。使功能與'如果'點免費

的任務是:

寫自由點功能numocc它計算元素出現在名單給出。例如:numocc 1 [[1, 2], [2, 3, 2, 1, 1], [3]] = [1, 2, 0]

這是我的代碼:

addif :: Eq a => a -> Int -> a -> Int 
addif x acc y = if x == y then acc+1 else acc 

count :: Eq a => a -> [a] -> Int 
count = flip foldl 0 . addif 

numocc :: Eq a => a -> [[a]] -> [Int] 
numocc = map . count 

numocccount是 '點免費',但他們使用的功能addif這是不。

我不知道我該怎麼辦這個功能addif免點。有沒有辦法做if聲明免費?也許有一個技巧,不使用if

+1

你被允許像'let fxy = 1 - ceiling(fromInteral(xy)/ fromIntegral y):: Int'那樣做數學chenigans嗎? (這不完全是你需要的;)) – Carsten

+0

,但'1 - 天花板(絕對$ fromInteral(xy)/ fromIntegral(max xy)):: Int'應該這樣做,如果我沒有錯過任何地方一些討厭的邊界情況 - 也許你會想要推理或運行一些快速檢查;)(以及我錯過了一些負面因素,所以你必須在更多的'abs'中...但原則應該是顯而易見的......真正的解決方案是*微不足道的*並留給讀者**:D **) – Carsten

+3

一般來說,你可以用函數'bool :: Bool - > a - > a - > a替換一個'if'語句; bool False f _ = f; bool True _ t = t',在這種情況下,您總是可以形成一個「常規」表達式,使用常規方法可以使其免於點。 – user2407038

回答

10

我會用事實,你可以很容易地轉換使用BoolIntfromEnum

addif x acc y = acc + fromEnum (x == y) 

現在可以開始申請慣用的招數,以使其指向自由

-- Go prefix and use $ 
addif x acc y = (+) acc $ fromEnum $ (==) x y 
-- Swap $ for . when dropping the last argument 
addif x acc = (+) acc . fromEnum . (==) x 

等等。我不會拿走所有的樂趣,特別是當有工具可以幫你時。

或者,你可以寫像

count x = sum . map (fromEnum . (==) x) 

的函數,而差一點點免費的,有一些讓你更接近的招數,雖然他們得到非常討厭迅速:

count = fmap fmap fmap sum map . fmap fmap fmap fromEnum (==) 

這裏我認爲它使用fmap而不是(.)實際上看起來更好,儘管你可以用(.)代替每個fmap,它將是完全相同的代碼。從本質上講,(fmap fmap fmap)組成一個單一的參數和雙參數功能一起,如果你不是給它.:你可以這樣寫

count = (sum .: map) . (fromEnum .: (==)) 

分佈看名稱:

> :t fmap fmap fmap sum map 
Num a => (a -> b) -> [a] -> b 

所以它需要一個功能從b到數字a,列表b s,並返回a,不算太壞。

> :t fmap fmap fmap fromEnum (==) 
Eq a => a -> a -> Int 

而這種類型可以寫成Eq a => a -> (a -> Int),這是一個重要的事情要注意。這使得該函數的返回類型與fmap fmap fmap sum map的輸入b ~ Int匹配,所以我們可以將它們組合起來以獲得類型爲Eq a => a -> [a] -> Int的函數。

6

一招是導入許多if functions中的一個,例如, Data.Bool.bool 1 0(也在Data.Bool.Extras中找到)。

更神祕的把戲是使用Foreign.Marshal.Utils.fromBool,這正是你在這裏所需要的。或者同樣的事情,少一些奧術:fromEnum(謝謝@bheklilr)。

但我認爲最簡單的把戲就是簡單地避免自己計算數字,並在filter ing之後應用標準的length函數。

+1

爲什麼不使用'fromEnum'而不是'Foreign.Marshal'函數?它做同樣的事情,已經存在於'Prelude'中。 – bheklilr

+0

@bheklilr:呃,沒有意識到'Bool'是'Enum'的一個實例 - 你已經得到了我的滿意答案:-)'fromBool'剛剛跨過我的腦海,我在過去的某個地方見過它... – Bergi

8

爲什麼不

numocc x 
    = map (length . filter (== x)) 
    = map ((length .) (filter (== x))) 
    = map (((length .) . filter) (== x)) 
    = map (((length .) . filter) ((==) x)) 
    = map (((length .) . filter . (==)) x) 
    = (map . ((length .) . filter . (==))) x 
    = (map . (length .) . filter . (==)) x 

再瑣碎的ETA-收縮。

+1

我認爲這最符合所描述的任務。 –

+0

@AndrásKovács後退方法...... :)比較[這個最近的Lisp條目](http://stackoverflow.com/questions/34207933/print-adjacent-duplicates-of-a-list-scheme)一些複雜的等價的地圖頭。過濾器(not.null.drop 1)。 group'。 –

1

使用的BoolEnum例如,它可以建立一個pointfree替代,如果能夠在更一般的情況下使用:

chk :: Bool -> (a,a) -> a 
chk = ([snd,fst]!!) . fromEnum 

使用chk我們可以定義不同版本的addIf

addIf' :: Eq a => a -> a -> Int -> Int 
addIf' = curry (flip chk ((+1),id) . uncurry (==)) 

現在,我們可以addIf'只需更換chk

addIf :: Eq a => a -> a -> Int -> Int 
addIf = curry (flip (([snd,fst]!!) . fromEnum) ((+1),id) . uncurry (==)) 
0

我認爲你正在尋找Data.Boolbool,它自4.7.0.0(2014-04-08)以來就存在。

incif :: (Eq a, Enum counter) => a -> a -> counter -> counter 
incif = ((bool id succ) .) . (==) 

附加.允許==採取兩個參數,傳遞表達式bool之前。

由於參數的順序是不同的,你需要使用incif這樣的:

(flip . incif) 

(集成即incif就留給讀者做練習[譯:這不是小事,並且我還不知道如何......]]