dan*_*tel 5 haskell coding-style
这是我的第一个Haskell程序.你会用更好的方式写什么部分?
-- Multiplication table
-- Returns n*n multiplication table in base b
import Text.Printf
import Data.List
import Data.Char
-- Returns n*n multiplication table in base b
mulTable :: Int -> Int -> String
mulTable n b = foldl (++) (verticalHeader n b w) (map (line n b w) [0..n])
where
lo = 2* (logBase (fromIntegral b) (fromIntegral n))
w = 1+fromInteger (floor lo)
verticalHeader :: Int -> Int -> Int -> String
verticalHeader n b w = (foldl (++) tableHeader columnHeaders)
++ "\n"
++ minusSignLine
++ "\n"
where
tableHeader = replicate (w+2) ' '
columnHeaders = map (horizontalHeader b w) [0..n]
minusSignLine = concat ( replicate ((w+1)* (n+2)) "-" )
horizontalHeader :: Int -> Int -> Int -> String
horizontalHeader b w i = format i b w
line :: Int -> Int -> Int -> Int -> String
line n b w y = (foldl (++) ((format y b w) ++ "|" )
(map (element b w y) [0..n])) ++ "\n"
element :: Int -> Int -> Int -> Int -> String
element b w y x = format (y * x) b w
toBase :: Int -> Int -> [Int]
toBase b v = toBase' [] v where
toBase' a 0 = a
toBase' a v = toBase' (r:a) q where (q,r) = v `divMod` b
toAlphaDigits :: [Int] -> String
toAlphaDigits = map convert where
convert n | n < 10 = chr (n + ord '0')
| otherwise = chr (n + ord 'a' - 10)
format :: Int -> Int -> Int -> String
format v b w = concat spaces ++ digits ++ " "
where
digits = if v == 0
then "0"
else toAlphaDigits ( toBase b v )
l = length digits
spaceCount = if (l > w) then 0 else (w-l)
spaces = replicate spaceCount " "
Run Code Online (Sandbox Code Playgroud)
eph*_*ent 11
你没有使用任何东西import Text.Printf.
在风格上,您使用的括号多于必要的括号.Haskellers倾向于在清除那些无关紧要的东西时发现代码更具可读性.h x = f (g x)写作而不是类似的东西h = f . g.
这里没有什么需要Int; (Integral a) => a应该这样做.
foldl (++) x xs== concat $ x : xs:我相信内置功能concat比你的实现更好.
此外,你应该更喜欢foldr当函数在第二个参数中是惰性的时候(++)- 因为Haskell是惰性的,这减少了堆栈空间(并且也适用于无限列表).
此外,unwords和unlines是快捷键intercalate " "和concat . map (++ "\n")分别,即"用空格加盟"和"用换行加入(加换行符)"; 你可以用那些替换一些东西.
除非你使用大数字,否则w = length $ takeWhile (<= n) $ iterate (* b) 1可能更快.或者,在懒惰的程序员的情况下,让w = length $ toBase b n.
concat ( (replicate ((w+1)* (n+2)) "-" )== replicate ((w+1) * (n+2)) '-'- 不确定你是如何错过这一个的,你只是把它弄好了几行.
你也做同样的事情concat spaces.但是,实际使用Text.Printf导入和写入不是更容易printf "%*s " w digits吗?
Nor*_*sey 11
以下是一些建议:
为了使计算的表格更明显,我将列表传递[0..n]给line函数而不是传递n.
我会进一步分析水平和垂直轴的计算,以便它们作为参数传递mulTable而不是在那里计算.
Haskell是高阶的,几乎没有计算与乘法有关.所以我会更改mulTableto 的名称binopTable并将实际的乘法作为参数传递.
最后,个别数字的格式是重复的.为什么不\x -> format x b w作为参数传递,不需要b和w?
我建议的更改的净效果是您构建一个通用的高阶函数来为二元运算符创建表.它的类型变得像
binopTable :: (i -> String) -> (i -> i -> i) -> [i] -> [i] -> String
Run Code Online (Sandbox Code Playgroud)
并且你最终可以使用更多可重用的函数 - 例如,布尔真值表应该是小菜一碟.
高阶和可重用是Haskell方式.
诺曼拉姆齐提出了出色的高级(设计)建议; 以下是一些低级别的:
concat (replicate i "-").为什么不replicate i '-'呢?[String] -> String,瞧你找到了concat.现在去替换所有这些折叠.unlines.实际上,这更适合您的需求.这是魔法!所以
-- Returns n*n multiplication table in base b
mulTable :: Int -> Int -> String
mulTable n b =
Run Code Online (Sandbox Code Playgroud)
变
mulTable :: Int -> Int -> String
mulTable size base =
Run Code Online (Sandbox Code Playgroud)
where子句中的调用者范围内,它可以使用调用者的变量,从而节省你的需要将一切都传递给它.所以
line :: Int -> Int -> Int -> Int -> String
line n b w y =
concat
$ format y b w
: "|"
: map (element b w y) [0 .. n]
element :: Int -> Int -> Int -> Int -> String
element b w y x = format (y * x) b w
Run Code Online (Sandbox Code Playgroud)
变
line :: Int -> Int -> Int -> Int -> String
line n b w y =
concat
$ format y b w
: "|"
: map element [0 .. n]
where
element x = format (y * x) b w
Run Code Online (Sandbox Code Playgroud)
line进入mulTable's where条款; imho,你应该.
where嵌套在另一个where条款中的条款令人不安,那么我建议改变你的缩进习惯.我的建议是使用始终为2或总是4个空格的一致缩进.然后,您可以轻松地在任何地方看到where另一个where所在的位置.好下面是它的样子(还有一些其他风格的变化):
import Data.List
import Data.Char
mulTable :: Int -> Int -> String
mulTable size base =
unlines $
[ vertHeaders
, minusSignsLine
] ++ map line [0 .. size]
where
vertHeaders =
concat
$ replicate (cellWidth + 2) ' '
: map horizontalHeader [0 .. size]
horizontalHeader i = format i base cellWidth
minusSignsLine = replicate ((cellWidth + 1) * (size + 2)) '-'
cellWidth = length $ toBase base (size * size)
line y =
concat
$ format y base cellWidth
: "|"
: map element [0 .. size]
where
element x = format (y * x) base cellWidth
toBase :: Integral i => i -> i -> [i]
toBase base
= reverse
. map (`mod` base)
. takeWhile (> 0)
. iterate (`div` base)
toAlphaDigit :: Int -> Char
toAlphaDigit n
| n < 10 = chr (n + ord '0')
| otherwise = chr (n + ord 'a' - 10)
format :: Int -> Int -> Int -> String
format v b w =
spaces ++ digits ++ " "
where
digits
| v == 0 = "0"
| otherwise = map toAlphaDigit (toBase b v)
spaces = replicate (w - length digits) ' '
Run Code Online (Sandbox Code Playgroud)