def String.words (s : String) := s.split Char.isWhitespace |>.filter (¬·.isEmpty) def String.lines (s : String) := s.splitOn "\n" |>.filter (¬·.isEmpty) def List.unlines (xs : List String) : String := xs.intersperse "\n" |> String.join def ex := "The heat bloomed\tin December\nas the carnival season\n kicked into gear.\nNearly helpless with sun and glare, I avoided Rio's brilliant\nsidewalks\n and glittering beaches,\npanting in dark corners\nand waiting out the inverted southern summer." #eval ex.words #eval ex.lines #eval IO.println ex.lines.unlines def sep : List a → List a → List a | a1::a1s, a2::a2s => a1::a2::sep a1s a2s | a1, _ => a1 def justify : Nat → List String → String | _, [word] => word | width, words => let wordsLength := List.sum (String.length <$> words) let spacesNeeded := width - wordsLength let numSpaces := words.length - 1 let smallSpaceLen := spacesNeeded / numSpaces let smallSpace := List.replicate smallSpaceLen ' ' let largeSpaces := spacesNeeded % numSpaces let smallSpaces := numSpaces - largeSpaces let largeSpace := ' '::smallSpace let spaces := List.replicate largeSpaces largeSpace ++ List.replicate smallSpaces smallSpace |>.map List.asString String.join $ sep words spaces #eval justify 1 "Hello, world, how is it going?".words #eval justify 1 "Hello, world, how is it going?".words |>.length def breakUp (width : Nat) (words : List String) : List (List String) := let (_, line, lines) := words.foldl (λ(left, line, lines) word ↦ if word.length < left then (left - (word.length + 1), line++[word], lines) else (width - word.length, [word], lines++[line])) (width, [], []) lines++[line] -- def breakUp (width : Nat) (words : List String) -- : List (List String) := -- if width = 0 then [] else go width words [] [] -- where -- go (left : Nat) (words line : List String) (lines : List $ List String) := -- match left, words, line, lines with -- | _, [], l, ls => ls++[l] -- | left, (w::ws), l, ls => -- if width < w.length -- then go width ws [w] ls -- else if left < w.length + 1 -- then go width (w::ws) [] (ls++[l]) -- else go (left - (w.length + 1)) ws (l++[w]) ls -- termination_by (left, words) def solve (width : Nat) := List.unlines ∘ List.map (justify width) ∘ breakUp width ∘ String.words #eval IO.println $ solve 40 ex def usage := "Usage: justify WIDTH" def fail : IO UInt32 := do IO.println usage; return 1 def main : List String → IO UInt32 | [width] => if let some width := width.toNat? then do let mut text := "" let stdin ← IO.getStdin while true do let line ← stdin.getLine if line.isEmpty then break text := text ++ line IO.println $ solve width text return 0 else fail | _ => fail