Очистка FizzBuzz

Я все еще изучаю Haskell, и мне было интересно, есть ли менее подробный способ выразить приведенное ниже утверждение с помощью 1 строки кода:

map (\x -> (x, (if mod x 3 == 0 then "fizz" else "") ++ 
 if mod x 5 == 0 then "buzz" else "")) [1..100]

Производит: [(1,""),(2,""),(3,"fizz"),(4,""),(5,"buzz"),(6,"fizz"),(7,""),(8,""),(9,"fizz"),(10,"buzz"),(11,""),(12,"fizz"),(13,""),(14,""),(15,"fizzbuzz"),(16,""),(17,""),(18,"fizz"),(19,""),(20,"buzz"),(21,"fizz"),(22,""),(23,""),(24,"fizz"),(25,"buzz"),(26,""),(27,"fizz"),(28,""),(29,""),(30,"fizzbuzz") и т. д.

Просто мне кажется, что я борюсь с синтаксисом больше, чем должен. Я видел другие вопросы для этого в Haskell, но я ищу наиболее оптимальный способ выразить это в одном выражении (пытаясь понять, как лучше работать с синтаксисом).


person Jonathan Dunlap    schedule 28.01.2012    source источник
comment
haskell.org/haskellwiki/   -  person Daniel Wagner    schedule 28.01.2012
comment
@Серджио, не говори глупостей. Вам просто нужно 2 кандидата наук :P   -  person Jonathan Dunlap    schedule 29.01.2012


Ответы (8)


Если вы настаиваете на однострочнике:

[(x, concat $ ["fizz" | mod x 3 == 0] ++ ["buzz" | mod x 5 == 0]) | x <- [1..100]]
person hammar    schedule 28.01.2012
comment
это кажется очень близким к тому, что я думаю. Есть ли способ уварить это, чтобы concat не требовался? Читая это логически, concat для меня является формой поведенческого шума. Он гласит: создайте массив с шагами от 1 до 100, где каждый элемент объединяется с двумя массивами, содержащими один строковый элемент, наложенный друг на друга (..etc). - person Jonathan Dunlap; 29.01.2012
comment
Кстати, списки в Haskell — это односвязные списки, а не массивы; массивы (например, в массиве и vector) используются относительно редко по сравнению с другими языками. - person ehird; 29.01.2012
comment
пометив это как ответ, поскольку он лучше всего читается как однострочное утверждение без составных утверждений, таких как «где». Мне также нравится, что выражение полностью построено внутри определения списка, что придает ему более семантический контекст. - person Jonathan Dunlap; 29.01.2012
comment
Вы можете использовать join вместо concat - person pat; 29.01.2012
comment
Если вы хотите избавиться от concat, замените соответствующее выражение на: [f | f <- "fizz", mod x 3 == 0] ++ [b | b <- "buzz", mod x 5 == 0]. Злоупотребление хаммаром пониманием списка теперь еще хуже. - person Rafael Caetano; 30.01.2012
comment
@Rafael Можете ли вы объяснить, почему это злоупотребление? Не зная много об этом, это, кажется, читается очень хорошо. - person Jonathan Dunlap; 31.01.2012
comment
Хм, если вы мало что знаете об этом, я думаю, что лучше сначала прочитать больше идиоматических примеров. Одним из примеров является [n | n <- [1..100], isPrime n]. Это возрастающая последовательность всех простых чисел от 1 до 100 (включительно). Guard относится к тому же n, что и в генераторе. Но в наших примерах значение в стороже зависит только от чего-то определенного вне понимания списка. И мой пример использует тот факт, что String — это просто список Char. Кстати, я не критикую ответ Хаммара. Вы просили короткую версию, не очень хорошую. ;-) - person Rafael Caetano; 01.02.2012

Нам не нужна вонючая mod...

zip [1..100] $ zipWith (++) (cycle ["","","fizz"]) (cycle ["","","","","buzz"])

или немного короче

import Data.Function(on)

zip [1..100] $ (zipWith (++) `on` cycle) ["","","fizz"] ["","","","","buzz"]

Или методом грубой силы:

zip [1..100] $ cycle ["","","fizz","","buzz","fizz","","","fizz","buzz","","fizz","","","fizzbuzz"]
person Landei    schedule 29.01.2012

Как насчёт...

fizzBuzz  =  [(x, fizz x ++ buzz x) | x <- [1..100]]
  where fizz n | n `mod` 3 == 0  =  "fizz"
               | otherwise       =  ""
        buzz n | n `mod` 5 == 0  =  "buzz"
               | otherwise       =  ""
person Fred Foo    schedule 28.01.2012

Не мог не пойти в другом направлении и не усложнить задачу. Смотри, нет mod...

merge as@(a@(ia,sa):as') bs@(b@(ib,sb):bs') =
  case compare ia ib of
    LT -> a : merge as' bs
    GT -> b : merge as  bs'
    EQ -> (ia, sa++sb) : merge as' bs'
merge as bs = as ++ bs

zz (n,s) = [(i, s) | i <- [n,2*n..]]
fizzBuzz = foldr merge [] $ map zz [(1,""), (3,"fizz"), (5,"buzz")]
person pat    schedule 28.01.2012

В том же духе, что и ответ Ларсмана:

fizzBuzz = [(x, f 3 "fizz" x ++ f 5 "buzz" x) | x <- [1..100]]
  where f k s n | n `mod` k == 0 = s
                | otherwise      = ""
person dave4420    schedule 28.01.2012
comment
это по сути то, что я только что сделал: [ (x, "Fizz" `ifDivisibleBy` 3 ++ "Buzz" `ifDivisibleBy` 5) | x <- [1..100], let ifDivisibleBy s n = if x `mod` n == 0 then s else "" ] - person rampion; 28.01.2012

Я думаю, причина, по которой вы чувствуете, что боретесь с синтаксисом, заключается в том, что вы смешиваете слишком много типов.

Вместо того, чтобы пытаться распечатать:

[(1, ""), (2,""), (3,"Fizz")...]

Просто подумайте о печати строк:

["1","2","Fizz"...]

Моя попытка:

Prelude> let fizzBuzz x | x `mod` 15 == 0 = "FizzBuzz" | x `mod` 5 == 0 = "Buzz" | x `mod` 3 == 0 = "Fizz" | otherwise = show x
Prelude> [fizzBuzz x | x <-[1..100]]

["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz"...]

Чтобы преобразовать Int в String, вы используете:

show x
person elviejo79    schedule 28.01.2012

Просто для учебы

zipWith (\a b -> b a) (map show [1..100]) $ cycle [id,id,const "fizz",id,const "buzz",const "fizz",id,id,const "fizz",const "buzz",id,const "fizz",id,id,const "fizzbuzz"]

производит

["1","2","fizz","4","buzz","fizz","7","8","fizz","buzz","11","fizz","13","14","fizzbuzz","16","17","fizz","19","buzz","fizz","22","23","fizz","buzz","26","fizz","28","29","fizzbuzz","31","32","fizz","34","buzz","fizz","37","38","fizz","buzz","41","fizz","43","44","fizzbuzz","46","47","fizz","49","buzz","fizz","52","53","fizz","buzz","56","fizz","58","59","fizzbuzz","61","62","fizz","64","buzz","fizz","67","68","fizz","buzz","71","fizz","73","74","fizzbuzz","76","77","fizz","79","buzz","fizz","82","83","fizz","buzz","86","fizz","88","89","fizzbuzz","91","92","fizz","94","buzz","fizz","97","98","fizz","buzz"]
person Sergei Iashin    schedule 24.05.2016

Монада Writer может выглядеть красиво (если вам не нравится concat):

fizzBuzz = [(x, execWriter $ when (x `mod` 3 == 0) (tell "fizz") >> when (x `mod` 5 == 0)  (tell "buzz")) | x <- [1..100]]

Хотя это не особенно лаконично.

person Rotsor    schedule 29.01.2012