challenge 文字列の均等分割

一行の文字列を指定した数の行にできるだけ文字数が均等になるように分割してください.
ただし,除算や剰余算を使わないで書いてみてください.

sample = "ゆめよりもはかなき世のなかをなげきわびつゝあかしくらすほどに四月十よひにもなりぬれば木のしたくらがりもてゆく"

divid 4 sample =>
 "ゆめよりもはかなき世のなかを"
 "なげきわびつゝあかしくらすほ"
 "どに四月十よひにもなりぬれ"
 "ば木のしたくらがりもてゆく"

divid 5 sample => 
 "ゆめよりもはかなき世の"
 "なかをなげきわびつゝあ"
 "かしくらすほどに四月十"
 "よひにもなりぬれば木の"
 "したくらがりもてゆく"

divid 6 sample => 
 "ゆめよりもはかなき"
 "世のなかをなげきわ"
 "びつゝあかしくらす"
 "ほどに四月十よひに"
 "もなりぬれば木のし"
 "たくらがりもてゆく"

この問題は、除算だけでははく算術演算とか、文字列の長さをstrlenの類いで測るとかをしなくても、多分書けるのではないかと思います。

Posted feedbacks - Haskell

文字列分のTrueの後にFalseをいくつかくっつけたリストを作り、nで分割した後transposeして、Trueの分だけ先頭から切り出す。

例えばdivid 3 "abcdefghijk"なら

 bools = [T,T,T,T,T,T,T,T,T,T,T,F,F,F]
 slices = [[T,T,T],[T,T,T],[T,T,T],[T,T,F]]
 transpose = [[T,T,T,T],[T,T,T,T],[T,T,T,F]]

boolsを無限リスト(Fが後ろに無限個付く)にしてもいけるかと思ったけど、無限リストのtransposeは止まらないみたい。そこまで見ないんだから止まってくれても良さそうな気がするけど…
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import Data.List

divid n cs = snd $ mapAccumL taker cs
                 $ transpose
                 $ slices
                 $ bools cs
 where
   slices xs | n > length xs = []
             | otherwise     = (take n xs):(slices $ drop n xs)
   bools cs = (map (const True) cs) ++ (replicate n False)
   taker (c:cs) (True:bs) = let (cs',frag) = (taker cs bs)
                              in (cs', c:frag)
   taker cs _             = (cs, [])

いや、そもそも後ろにFalseをくっつける必要が無かった。takeはリストの方が短いと結果も短くしてくれるのね。(Gaucheのtake*の動作)

1
2
3
4
5
6
7
8
import Data.List

divid n cs = snd $ mapAccumL taker cs $ transpose $ slices cs
 where
   slices [] = []
   slices xs = (take n xs):(slices $ drop n xs)
   taker cs []         = (cs, [])
   taker (c:cs) (b:bs) = let (cs',frag) = (taker cs bs) in (cs', c:frag)

私の用意していたものはshiroさんの解と本質的に同じでした。
slices と taker にも汎用性がありそうなのでトップレベルでの定義にしてあります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import Data.List
import qualified System.IO.UTF8 as U

divid :: Int -> [a] -> [[a]]
divid n xs 
  = snd $ mapAccumL ((swap .) . flip splitWith) xs $  transpose $ slices n xs

swap :: (a,b) -> (b,a)
swap (x,y) = (y,x)

splitWith :: [b] -> [a] -> ([a],[a])
splitWith _  [] = ([],[])
splitWith [] xs = ([],xs)
splitWith (x:xs) (y:ys) = case splitWith xs ys of (zs,ws) -> (y:zs,ws)

slices :: Int -> [a] -> [[a]]
slices n = unfoldr phi
  where 
    phi [] = Nothing
    phi xs = Just $ splitAt n xs

Index

Feed

Other

Link

Pathtraq

loading...