Haskell learning
2023.10.5
在了解基本概念后,语言学习的最佳方法还是刷题。刷题并对比自己和他人的解答能教给你一些在代码编写的 "best practice" ,见多了这些 "best practice" 方才更有信心去做项目/读项目代码。
最近在学习 Haskell,就在 codewars 上找一些题目学习一下。
Highest and Lowest
https://www.codewars.com/kata/554b4ac871d6813a03000035/haskell
慌忙 google 各种常用函数后得到如下答案(其中最大的问题是不知道如何给 expr 类型标注,所以把 read 封装成了 stringToInt):
stringToInt :: String -> Int
stringToInt = read
highAndLow :: String -> String
highAndLow input =
let strs = words input in
let nums = map stringToInt strs in
show (maximum nums) ++ " " ++ show (minimum nums)
更好的解答1:
highAndLow :: String -> String
highAndLow xs = show (maximum ns) ++ " " ++ show (minimum ns)
where ns = (map read $ words xs) :: [Int]
-
where 和 let 似乎差不多... 但若是 预备工作太多 还是放在 where 里更好一些,因为 where 置后能更好凸显代码主体...
-
对 expr 的类型标注用
::
就可以了,所以 上面的stringToInt
可以替换为(read :: String -> Int)
更好的解答2:
highAndLow :: String -> String
highAndLow = unwords . map show . sequence [maximum,minimum] . map (read ::String->Int) . words
一行流...
unwords: words 的 inverse
sequence: Evaluate each monadic action in the structure from left to right, and collect the results. The Traversable
class 的函数; class (Functor t, Foldable t) => Traversable t
- https://blog.jakuba.net/2014-07-30-foldable-and-traversable/
- https://downloads.haskell.org/~ghc/5.04.1/docs/html/base/index.html
- https://wiki.haskell.org/Foldable_and_Traversable
- !!! Monad ((->) r) https://hackage.haskell.org/package/base-4.14.1.0/docs/src/GHC.Base.html#line-979, 这个 Monad 可以理解为 Map?
- 嗯...总之还是非常抽象...
解答3:
highAndLow :: String -> String
highAndLow = unwords . map show . f . map read . words
where f :: [Integer] -> [Integer]
f xs = [maximum xs, minimum xs]
上面解答中烧脑的 sequence [maximum,minimum]
换成 f
解答4:
highAndLow :: String -> String
highAndLow input =
let ns = map read $ words input :: [Int]
mx = maximum ns
mn = minimum ns
in
unwords $ map show [mx,mn]
很规整的一个解答
解答5:
highAndLow :: String -> String
highAndLow = unwords . map show . highAndLow' . map read . words
highAndLow' :: [Int] -> [Int]
highAndLow' (n1:ns) = foldl (\[mx, mn] n -> [max mx n, min mn n]) [n1, n1] ns
稍微快点,但就没那么好看了
Multiply
https://www.codewars.com/kata/50654ddff44f800200000004/haskell
但感觉应该不是考点...
考点应该是如下解答中涉及的:
Tribonacci Sequence
https://www.codewars.com/kata/556deca17c58da83c00002db/haskell
tribonacci :: (Num a) => (a, a, a) -> Int -> [a]
tribonacci (a, b, c) n =
take n $
map (\(x, _, _) -> x) ns
where
ns = iterate (\(x, y, z) -> (y, z, x + y + z)) (a, b, c)
emm...还是很麻烦的解答
解答1:
tribonacci :: Num a => (a, a, a) -> Int -> [a]
tribonacci _ n | n < 1 = []
tribonacci (a, b, c) n = a : tribonacci (b, c, a+b+c) (n-1)
emmmm.... very very clever!!!
在做本题的时候脑子里就没有产生过递归这个念头...被 wholemeal programming 毒害了么...
解答2:
tribonacci :: Num a => (a, a, a) -> Int -> [a]
tribonacci (a, b, c) n = take n tribs
where tribs = [a, b, c] ++ zipWith3 (\x y z -> x + y + z) tribs (tail tribs) (tail $ tail tribs)
-- 或类似的
tribonacci :: Num a => (a, a, a) -> Int -> [a]
tribonacci (a, b, c) n = take n $ trib
where trib = a : b : c : zipWith3 (\a b c -> a + b + c)
(drop 0 trib)
(drop 1 trib)
(drop 2 trib)
人家也是 wholemeal programming,人家为什么这么优雅?!
噢,tribs 可以直接递归定义啊...
https://www.scs.stanford.edu/16wi-cs240h/slides/basics-slides.html#(8)
解答3:
tribonacci :: Num a => (a, a, a) -> Int -> [a]
tribonacci sig n = take n $ unfoldr (\(a,b,c) -> Just (a,(b,c,a+b+c))) sig
还有 unfoldr
这么高级的函数吗...
https://hackage.haskell.org/package/deferred-folds-0.9.18.3/docs/DeferredFolds-Unfoldr.html
解答4:
{-# LANGUAGE BangPatterns #-}
tribonacci :: Num a => (a, a, a) -> Int -> [a]
tribonacci (a, b, c) n = take n . go $ (a, b, c)
where
go (!x, !y, !z) = x : go (y, z, x + y + z)
BangPattern: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/strict.html , 不 lazy 了
其余和解法一类似
其它
https://en.wikibooks.org/wiki/Haskell/Understanding_monads
发现一门非常好的课程...决定先看这个了 https://www.scs.stanford.edu/16wi-cs240h/sched/