Haskell箭头教程循环/状态

时间:2017-06-19 13:38:54

标签: haskell

从此 https://en.wikibooks.org/wiki/Haskell/Arrow_tutorial#Hangman:_Main_program

IO如何完成?格外

main :: IO ()
main = do
    rng <- getStdGen
    interact $ unlines                      -- Concatenate lines out output
        . ("Welcome to Arrow Hangman":)     -- Prepend a greeting to the output
        . concat . map snd . takeWhile fst  -- Take the [String]s as long as the first element of the tuples is True
        . runCircuit (hangman rng)          -- Process the input lazily
        . ("":)                             -- Act as if the user pressed ENTER once at the start
        . lines                             -- Split input into lines

Interact似乎是(string - &gt; string) - &gt; IO()。印象是它打印的是每行所读取的功能。令我困惑的是,如何打印初始线。存储在两者之间的状态在哪里?。

早先以一种已经生成所有输入的方式使用了runCircuit。我对这个版本如何逐行运行感到困惑,但似乎没有存储任何状态?

Circuit String (Bool, [String])如何以runCircuit :: Circuit a b -> [a] -> [b]逐行方式运行?以某种方式似乎记得以前的结果在哪里?。

1 个答案:

答案 0 :(得分:3)

Interact不会每行运行该函数。 interactrunCircuit是懒惰的。因为您在行中拆分输入并连接输出,所以当您提供越来越多的输入时,您将看到runCircuit的进度。

函数runCircuit定义如下:

runCircuit :: Circuit a b -> [a] -> [b]
runCircuit _   []     = []
runCircuit cir (x:xs) =
    let (cir',x') = unCircuit cir x
    in  x' : runCircuit cir' xs

在那里,您可以看到在输入列表中为输入列表中的每个元素(每行)生成一个元素。这已经表明您将能够懒惰地处理列表。 (为了进行比较:如果需要xs的长度来生成第一个输出x',那么runCircuit变得懒惰。)

让我们将其与Circuit

的定义结合起来
data Circuit a b = Circuit { unCircuit :: a -> (Circuit a b, b) }

您运行电路的方式是,您提供x类型的第一个输入a,不仅可以获得x'类型的第一个输出b,还可以获得延续 Circuitcir'中的runCircuit)。此延续是新的Circuit a b,由runCircuit在下一次迭代中使用。这就是状态的保留方式:新的Circuit将与原始的mySum :: Circuit Int Int mySum = mySum' 0 mySum' :: Int -> Circuit Int Int mySum' acc = Circuit $ \input -> let acc' = acc + input in (mySum' acc', acc') 类似,但它可能受到先前输入的影响。

例如,您可以定义一个对Ints求和并产生总和的电路。这篇文章中有一个例子,但是要使事情变得非常简单:

Circuit

在每次迭代中,返回的继续mySum' acc' acc'使用新的累加器Circuit,其中包含到该点的总和。所以这个accum :: acc -> (a -> acc -> (b, acc)) -> Circuit a b accum acc f = Circuit $ \input -> let (output, acc') = input `f` acc in (accum acc' f, output) 保持状态,因为它记住或结转到那时所有数字的总和。

回到那篇文章,稍微更通用的功能:

Circuit

在元组的第一个与自身不同的参数中返回 continuation accum acc f。它被称为accum acc' f,但延续为acc',其中input取决于acc<?php $a = [ 85, 85167920, 'ELECTRICAL/ELECTRONIC', 'DEVICES', 'FOR', 'REPELLING', 'INSECTS', '(E.G.MOSQUITOES ETC)' ]; $b = [ 85, 851680, 'ELECTRIC', 'HEATING', 'RESISTORS' ]; $aa = array_slice($a, 0,2); $aa[] = implode(' ',array_slice($a, 2)); $bb = array_slice($b, 0,2); $bb[] = implode(' ',array_slice($b, 2)); print_r($aa); echo '<br>'; print_r($bb); ?> ,因此它会在此累加器中保留内存。 / p>

使用延续是非常非常常见的。我认为大多数管道/流处理框架和许多FRP实现都是这样做的,包括Yampa,Varying,Dunai和netwire。