我想要做的并不是真正解决问题,而是学习如何编写构成/利用基本功能的Haskell代码来实现它。
我有一个函数,它接受元组(String,Int)和String的列表,并返回一个元组,其fst与给定的String匹配。 这对filter和lambda来说相当容易,但我现在要做的是删除最右边的参数,即。我想重构函数,使其成为部分应用函数的组合,这些函数将执行相同的功能。
原始代码是:
getstat :: Player -> String -> Stat
getstat p n = head $ filter (\(n', v) -> n' == n) $ stats p
新代码是:
getstat :: Player -> String -> Stat
getstat p = head . (flip filter $ stats p) . cmpfst
where cmpfst = (==) . fst . (flip (,)) 0 -- Wrong :-\
想法是翻转过滤器并通过给出元组列表(stats p)来部分应用,然后组成cmpfst。 cmpfst应该是String - > (String,Int) - > Bool,以便在应用String参数时,它变为 - > Bool对于过滤器传入元组有好处,但正如你所看到的 - 我在编写(==)时遇到问题,因此只比较了给定元组的fst。
P.S。我知道第一个代码可能更清晰;这项任务的重点不是编写干净的代码,而是学习如何通过合成解决问题。
编辑:
我很清楚,在一个可能空的列表上寻找一个头是一个糟糕的编程,这将导致崩溃。就像前面提到的一张海报一样,用Maybe monad非常简单而优雅地解决了这个问题 - 这是我以前做过的一项熟悉的任务。
我希望重点关注的是如何使cmpfst主要由基本功能组成。 到目前为止,我得到的最远的是:
getstat :: Player -> String -> Stat
getstat p = head . (flip filter $ stats p) . (\n' -> (==(fst n')) . fst) . (flip (,)) 0
我无法通过合成和部分应用(==)来摆脱(a - > Bool)lambda。对我来说,这表明我要么不明白我在做什么,要么以我想象的方式使用(==)运算符是不可能的。
此外,除非没有确切的解决方案,否则我会接受签名更改解决方案作为正确解决方案。我不想改变函数的签名只是因为它是我的心理练习,而不是生产代码。
答案 0 :(得分:2)
如果我正在写这个函数,我可能会给它这个类型的签名:
getstat :: String -> Player -> Stat
这使得将定义简化为
变得容易getstat n = head . filter ((== n) . fst) . stats
答案 1 :(得分:1)
在评论中,您已达到
basic_string( const CharT* s,
size_type count,
const Allocator& alloc = Allocator() );
我想知道是否有更好的成分可以消除anon f。
嗯,这是
getstat p = head . (flip filter $ stats p) . (\n (n', v) -> n' == n)
删除\n (n', v) -> n' == n
-- for convenience, we flip the ==
\n (n', v) -> n == n'
-- prefix notation
\n (n', v) -> (==) n n'
-- let's remove pattern matching over (n', v)
\n (n', v) -> (==) n $ fst (n', v)
\n x -> (==) n $ fst x
-- composition, eta
\n -> (==) n . fst
-- prefix
\n -> (.) ((==) n) fst
-- composition
\n -> ((.) . (==) $ n) fst
-- let's force the application to be of the form (f n (g n))
\n -> ((.) . (==) $ n) (const fst $ n)
-- exploit f <*> g = \n -> f n (g n) -- AKA the S combinator
((.) . (==)) <*> (const fst)
-- remove unneeded parentheses
(.) . (==) <*> const fst
作为练习。