对于一个Lisp类,我们得到了一个简单的行转置密码作业,我试图在Haskell中解决它。基本上,只需将字符串拆分为长度为n
的行,然后转换结果。生成的字符列表列表的串联是加密字符串。解码有点困难,因为输入的最后一行可能缺少元素(结果中的列不完整),必须注意这些元素。
这是我在Haskell中的解决方案:
import Data.List
import Data.Ratio
import Data.List.Split
encode :: String -> Int -> String
encode s n = concat . transpose $ chunk n s
decode :: String -> Int -> String
decode s n = take len $ encode s' rows
where s' = foldr (insertAt " ") s idxs
rows = ceiling (len % n)
idxs = take (n-filled) [n*rows-1,(n-1)*rows-1..]
filled = len - n * (rows - 1)
len = length s
insertAt :: [a] -> Int -> [a] -> [a]
insertAt xs i ys = pre ++ xs ++ post
where (pre,post) = splitAt i ys
它完成了这项工作,但我不确定,这是否会被认为是惯用的Haskell,因为我对索引的摆弄不会过于夸张。这可以改进,如果是的话,怎么样?
顺便说一下:在Haskell 98中是否有类似insertAt
的内容?即将给定索引处的元素或列表插入列表的函数。
注意:这不是今天应该完成的作业的一部分。
答案 0 :(得分:6)
我会通过略微区别地查看encode
和decode
问题来做到这一点。 encode
将数据分解为n
- 列矩阵,然后将其转置(转换为n
- 行矩阵)并按行连接。 decode
将数据拆分为n
行矩阵,然后将其转置(转换为n
柱状矩阵)并按行连接。
所以我首先定义两个函数 - 一个用于将数组转换为n
列矩阵:
chunk:: Int -> [a] -> [[a]]
chunk n as = chunk' n (length as) as
where chunk' n l as | l <= n = [as]
| otherwise = some : chunk' n (l-n) rest
where (some, rest) = splitAt n as
和另一个将数组切割成n
行矩阵:
slice :: Int -> [a] -> [[a]]
slice n as = chunk (q+1) front ++ chunk q back
where (q,r) = length as `divMod` n
(front, back) = splitAt (r*(q+1)) as
现在,编码和解码相当容易:
encode :: Int -> [a] -> [a]
encode = ((concat . transpose) .). chunk
decode :: Int -> [a] -> [a]
decode = ((concat . transpose) .). slice