我一直在试用Haskell已经有一段时间了,我发现了一个有趣的问题:
我必须以非常特别的方式生成字符串的所有排列:
假设我有这个字符串:
let input = "_h_i_"
我想在input
中得到所有下划线的排列,保持其他字符的实际顺序,如下所示:
let results = ["hi___", "h_i__", "h__i_", "h___i", "_hi__", "_h_i_", "_h__i", "__hi_", "__h_i", "___hi"]
我不知道如何做到这一点。
答案 0 :(得分:1)
执行此操作的一种方法是生成所有排列,但只保留那些保留非下划线字符顺序的排列。
import Data.List
orderedPermutationsOf :: Char -> String -> [String]
orderedPermutationsOf c s =
let except = filter (/= c)
in nub $ filter (\x -> except s == except x) $ permutations s
它并不是非常高效,但它可以完成工作。
答案 1 :(得分:1)
so :: String -> [String]
so = takeWhile' notDone . iterate shift . preproccess
where
preproccess xs = (replicate (uCount xs) '_') ++ (letters xs)
uCount = length . filter (=='_')
letters = filter (/='_')
notDone = not . and . map (== '_') . dropWhile (/='_')
takeWhile' :: (a -> Bool) -> [a] -> [a]
takeWhile' _ [] = []
takeWhile' f (x:xs)
| f x = x:(takeWhile' f xs)
| otherwise = [x]
shift :: String -> String
shift [] = []
shift (x:xs)
| x /= '_' = roll lead [] rest
| otherwise = roll [] [] (x:xs)
where
(lead,rest) = span (/='_') (x:xs)
roll _ acc [] = acc
roll _ acc [a] = acc++[a]
roll ins acc (a:b:bs)
| (a == '_') && (b /= '_') = acc++ins++(b:a:bs)
| otherwise = roll ins (acc++[a]) (b:bs)
可能不是最漂亮的代码,但似乎有效。该算法基本上与您手动执行的算法相同,但反之亦然。
shift
通过滚动字符串并将最左边的非下划线字符向左移动来工作。重复此操作,直到所有非下划线字符都在左侧。
预处理只是为了获得他留下的所有下划线以及右边的所有其他字符。
答案 2 :(得分:1)
这是另一种解决方案
u='_'
perms :: String -> Int -> [String]
perms x 0 = [x]
perms [x] n = rotations $ x: (replicate n u)
perms (x:xs) n = (map (x:) $ perms xs n) ++ if(x/=u) then perms (u:x:xs) (n-1) else []
rotate xs n = zipWith const (drop n (cycle xs)) xs
rotations x = map (rotate x) [1..length x]
我更改了输入签名,用作
> perms "hi" 3
["h___i","h__i_","h_i__","hi___","_h__i","_h_i_","_hi__","__h_i","__hi_","___hi"]
我认为有一个更简单的解决方案隐藏在那里,但现在没有足够的时间。