Как я могу выразить этот цикл Python for в Haskell?

Иногда, когда я хочу использовать wget, я просто печатаю кучу строк с помощью Python, например:

>>> for i in range(25):
...   print "http://www.theoi.com/Text/HomerOdyssey", i, ".html"
... 
http://www.theoi.com/Text/HomerOdyssey 0 .html
http://www.theoi.com/Text/HomerOdyssey 1 .html
http://www.theoi.com/Text/HomerOdyssey 2 .html
http://www.theoi.com/Text/HomerOdyssey 3 .html
http://www.theoi.com/Text/HomerOdyssey 4 .html
http://www.theoi.com/Text/HomerOdyssey 5 .html
http://www.theoi.com/Text/HomerOdyssey 6 .html
http://www.theoi.com/Text/HomerOdyssey 7 .html
http://www.theoi.com/Text/HomerOdyssey 8 .html
http://www.theoi.com/Text/HomerOdyssey 9 .html
http://www.theoi.com/Text/HomerOdyssey 10 .html
http://www.theoi.com/Text/HomerOdyssey 11 .html
http://www.theoi.com/Text/HomerOdyssey 12 .html
http://www.theoi.com/Text/HomerOdyssey 13 .html
http://www.theoi.com/Text/HomerOdyssey 14 .html
http://www.theoi.com/Text/HomerOdyssey 15 .html
http://www.theoi.com/Text/HomerOdyssey 16 .html
http://www.theoi.com/Text/HomerOdyssey 17 .html
http://www.theoi.com/Text/HomerOdyssey 18 .html
http://www.theoi.com/Text/HomerOdyssey 19 .html
http://www.theoi.com/Text/HomerOdyssey 20 .html
http://www.theoi.com/Text/HomerOdyssey 21 .html
http://www.theoi.com/Text/HomerOdyssey 22 .html
http://www.theoi.com/Text/HomerOdyssey 23 .html
http://www.theoi.com/Text/HomerOdyssey 24 .html
>>> 

Я могу вставить этот вывод в новый файл, удалить пробелы и использовать wget -i.

Но я устал от Python.

Я хочу изучить Haskell.

Несмотря на то, что я потратил 10 минут на попытки сделать то же самое из ghci, я не продвинулся дальше.

Вот как выглядели мои попытки:

alec@ROOROO:~/oldio$ ghci
GHCi, version 7.0.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> putStrLn

<interactive>:1:1:
    No instance for (Show (String -> IO ()))
      arising from a use of `print'
    Possible fix:
      add an instance declaration for (Show (String -> IO ()))
    In a stmt of an interactive GHCi command: print it
Prelude> putStrLn "hey"
hey
Prelude> putStrLn "hey" [1..10]

<interactive>:1:1:
    The function `putStrLn' is applied to two arguments,
    but its type `String -> IO ()' has only one
    In the expression: putStrLn "hey" [1 .. 10]
    In an equation for `it': it = putStrLn "hey" [1 .. 10]
Prelude> putStrLn "hey" snd [1..10]

<interactive>:1:1:
    The function `putStrLn' is applied to three arguments,
    but its type `String -> IO ()' has only one
    In the expression: putStrLn "hey" snd [1 .. 10]
    In an equation for `it': it = putStrLn "hey" snd [1 .. 10]
Prelude> putStrLn "hey" $ snd [1..10]

<interactive>:1:1:
    The first argument of ($) takes one argument,
    but its type `IO ()' has none
    In the expression: putStrLn "hey" $ snd [1 .. 10]
    In an equation for `it': it = putStrLn "hey" $ snd [1 .. 10]
Prelude> "hello"
"hello"
Prelude> "hello" ++ "world"
"helloworld"
Prelude> "hello" ++ [1..10] ++ " world"

<interactive>:1:16:
    No instance for (Num Char)
      arising from the literal `10'
    Possible fix: add an instance declaration for (Num Char)
    In the expression: 10
    In the first argument of `(++)', namely `[1 .. 10]'
    In the second argument of `(++)', namely `[1 .. 10] ++ " world"'
Prelude> "hello" ++ print [1..10] ++ " world"

<interactive>:1:12:
    Couldn't match expected type `[Char]' with actual type `IO ()'
    In the return type of a call of `print'
    In the first argument of `(++)', namely `print [1 .. 10]'
    In the second argument of `(++)', namely
      `print [1 .. 10] ++ " world"'
Prelude> print [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> map ("hello") [1..10]

<interactive>:1:6:
    Couldn't match expected type `a0 -> b0' with actual type `[Char]'
    In the first argument of `map', namely `("hello")'
    In the expression: map ("hello") [1 .. 10]
    In an equation for `it': it = map ("hello") [1 .. 10]
Prelude> greeting :: String --> Int  -> [String, Int]

<interactive>:1:39: parse error on input `,'
Prelude> greeting :: String --> Int  -> [(String), (Int)]

<interactive>:1:41: parse error on input `,'
Prelude> greeting :: String -> Int  -> [(String), (Int)]

<interactive>:1:40: parse error on input `,'
Prelude> greeting :: String -> Int  -> [(String) (Int)]

<interactive>:1:1: Not in scope: `greeting'
Prelude> foreach [1..24] print

<interactive>:1:1: Not in scope: `foreach'
Prelude> import Data.IORef
Prelude Data.IORef> foreach [1..24] print

<interactive>:1:1: Not in scope: `foreach'
Prelude Data.IORef> foreach = flip mapM_

<interactive>:1:9: parse error on input `='

person magnetar    schedule 22.05.2012    source источник
comment
Как вы обнаружили, ввод-вывод Haskell - это не 10-минутная тема ...   -  person Luis Casillas    schedule 22.05.2012
comment
@sacundim putStrLn "hey" vs putStrLn "hey" [1..10] - это не проблема ввода-вывода - у него еще нет управляющих структур.   -  person Don Stewart    schedule 22.05.2012
comment
Обратите внимание, что ваш пример - довольно плохой стиль Python.   -  person Marcin    schedule 22.05.2012
comment
Вы можете избежать появления пробелов в Python с помощью print "http://www.theoi.com/Test/HomerOdyssey{0}.html".format(i) (среди многих других способов)   -  person sdcvvc    schedule 23.05.2012


Ответы (4)


mapM_ (\i -> putStrLn (concat ["http://www.theoi.com/Text/HomerOdyssey", show i, ".html"])) [0..24]

В качестве бонуса здесь не печатаются пробелы.

Теперь немного теории:

  • putStrLn - это функция, которая принимает единственный аргумент. Python, Perl и т. Д. Соберут все аргументы, которые вы дадите print, и превратят их в одну строку. В Haskell вы должны сделать это сами.
  • mapM_ принимает два аргумента. Во-вторых, список и, во-первых, функция, которая mapM_ по очереди передает каждый элемент списка. Функция, которую мы здесь передали, является анонимной функцией (например, лямбда в Python).
person dave4420    schedule 22.05.2012

for i in range(25):
...   print "http://www.theoi.com/Text/HomerOdyssey", i, ".html"

становится:

импорт Control.Monad

то что мы можем:

 forM_ [1..25] $ \i ->
     putStrLn $ "http://www.theoi.com/Text/HomerOdyssey" ++ show i ++ ".html"
person Don Stewart    schedule 22.05.2012
comment
Это может помочь объяснить, что такое $. - person Marcin; 22.05.2012
comment
comment
Это может помочь объяснить это в вашем ответе. - person Marcin; 22.05.2012
comment
Оператор $ предназначен для исключения скобок. Он помещает в скобки все, что находится справа от него. Он избегает вырезок, подобных Lisp. - person Don Stewart; 22.05.2012

Рассмотрите возможность использования списка:

mapM_ putStrLn ["http://www.theoi.com/Text/HomerOdyssey" ++ show i ++ ".html" | i <- [0..24]]
person sdcvvc    schedule 22.05.2012

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

В ghci вы можете использовать :t и :info для отображения типа чего-либо и информации о чем-то соответственно.

Prelude> :t putStrLn
putStrLn :: String -> IO ()
Prelude> :info putStrLn
putStrLn :: String -> IO ()     -- Defined in `System.IO'
Prelude> :t putStrLn "Hello, World"
putStrLn "Hello, World" :: IO ()

:t полезен, потому что вы можете использовать его для отображения типа произвольных выражений, чего :info не будет. Но :info может предоставить информацию о вещах, не имеющих типов, например о самих типах или конструкторах типов:

Prelude> :info IO
newtype IO a
  = GHC.Types.IO (GHC.Prim.State# GHC.Prim.RealWorld
                  -> (# GHC.Prim.State# GHC.Prim.RealWorld, a #))
    -- Defined in `GHC.Types'
instance Monad IO -- Defined in `GHC.Base'
instance Functor IO -- Defined in `GHC.Base'

Когда вы пытаетесь понять, как заставить ghci что-то принять, :t может оказаться очень полезным.

Затем поймите, что приглашение ghci дает вам построчную нотацию в вводе-выводе. Если вы введете что-то типа IO x, этот оператор будет вычислен, когда вы нажмете клавишу ВВОД. Если вы хотите привязать этот x, используйте стрелки-обозначения. Если вы хотите создать новую привязку (т.е. создать новую функцию или идентификатор), используйте let. Если вы введете выражение, которое не является вводом-выводом, ghci попытается show его вам, что не сработает, если Show экземпляр не определен.

Prelude> putStrLn "Hello, World"
Hello, World
Prelude> :t getChar
getChar :: IO Char
Prelude> x <- getChar
yPrelude> 
Prelude> show x
"'y'"
Prelude> let g = 10 :: Int
Prelude> :t g
g :: Int
Prelude> show g
"10"
Prelude> g
10

Обратите внимание, что show g и g - не одно и то же. Вы, вероятно, сможете понять, почему, из поведения, которое я описал выше.

person John L    schedule 23.05.2012