2016-09-17 3 views
0

Я пишу функцию 'printLine', которая - с учетом списка ints - возвращает строку, содержащую горизонтальную линию. Длина этой строки должна быть максимальным значением списка int. Например:Haskell print horizontal line

printLine [1, 3, 4, 0] 

должен вернуться:

+----+----+----+----+ 

(Обратите внимание, что длина определяется количеством черточек, а не на '+' - знаки Там всегда должно быть 5 '+' -. знаки)

я написал следующий код:.

printLine :: [Int] -> String 
printLine widthList = concat $ concat $ foldr (:) [["+"]] boundList 
where boundList = replicate 4 ("+" : hyphenList) 
     hyphenList = replicate max "-" 
     max = maximum widthList 

код работает правильно, однако, я чувствую, как Я слишком усложнил эту функцию. Например, я использовал «concat» дважды в функции. Есть ли «более чистый» способ решить эту проблему?

ответ

3

Часть осложнения может произойти, потому что функция пытается сделать слишком много за один раз. Очень маловероятно, что вы будете повторно использовать именно эту функцию в ближайшее время. Более того, число 4 и, возможно, даже символы «+» и «-» - «магические числа».

Поэтому первым шагом может быть, чтобы включить функцию в более многоразовый одной

-- | renamed from "printLine" because "print" has a connotation of I/O in Haskell 
separator :: Int -> String 
separator = repeatedLine 4 

repeatedLine :: Int -> Int -> String 
repeatedLine reps segmentWidth = ... 

Даже если вы никогда не используйте повторно функцию, номер-теперь имеет имя (reps), который идентифицирует его цель.

На основной функции. Ваша складка - это как reverse, так и ["+"]:. изменение необходимо, потому что boundList добавляет '+' в начале. Это хорошая оптимизация, но это делает комбинацию более сложной. Однако вы это делаете, кажется, у вас есть все эти детали для обработки. Но есть альтернативная перспектива: начать с бесконечности. Итак, у вас уже есть

where segment = '+':replicate segmentWidth '-' 

Теперь вы хотите повторить это. Самый простой способ сделать это - cycle segment, который создает бесконечный список сегментов.Теперь все, что вам нужно сделать, это взять правый префикс - который переводит всю сложность в несколько бит математики.

repeatedLine :: Int -> Int -> String 
repeatedLine reps segmentWidth = take (1+reps*(segmentWidth+1)) $ cycle segment 
    where segment = '+':replicate segmentWidth '-' 

Бонус: у вас теперь минимальное количество итераций.

0

Некоторые замечания:

  • В hyphenList = replicate max "-": "-" является строкой, так hyphenList будет список строк. Было бы проще, если бы вы использовали '-'. Это сделало бы строку hyphenList, и это поможет вам избавиться от одного из concat s.
  • Я бы переименовал переменную max, потому что уже существует функция, называемая max, определенная в Prelude.
  • Вы можете использовать функцию intercalate от Data.List, чтобы упростить складку и другие concat.
  • intercalate не будет добавлять разделители в начале и в конце сгенерированной строки, поэтому их необходимо добавить вручную.

Так положить все это вместе, вы получите:

printLine :: [Int] -> String 
printLine widthList = "+" ++ intercalate "+" boundList ++ "+" 
    where boundList = replicate 4 hyphenList 
     hyphenList = replicate m '-' 
     m = maximum widthList 
+1

Вы пропускаете знаки плюс в начале и в конце концов - вы получите их, если вы сделали интеркаляцию наоборот, как ' интеркалировать (повторить 4 '-') (повторить 5 "+") ' –

+0

@DavidFletcher Это хорошая идея. Я исправил проблему по-другому, чтобы оставаться как можно ближе к ОП. – redneb

1

комментарий @David Флетчер приводит к хорошему решению:

printLine :: [Int] -> String 
printLine xs = intercalate (replicate (maximum xs) '-') (replicate 5 "+")