如何在Haskell中编写Perl风格的循环?

时间:2014-10-24 02:55:58

标签: haskell for-loop

我刚开始学习Haskell。我想知道是否有人可以帮助我进行for-style循环。在Perl中,这就是我能做的:

#!/usr/bin/perl
my $total = 0;
for (my $n=0, my $i=1;  $i<=10; $i++, $n+=50) {
    $total += $n;
    print "iteration:$i\t", "n:$n\t", "total:$total", "\n"; 
}

如何在Haskell中获得相同的输出?谢谢。

2 个答案:

答案 0 :(得分:2)

所以看起来你的代码打印出部分总和。所以,让我们这样做!

您从$n开始迭代0,然后递增50十次。而不是将其作为循环,更多的Haskelly方法是创建一个包含n的所有值的列表。

-- take the first 10 elements from the sequence that starts 0, 50, 100, 150...
let ns = take 10 [0, 50..]

现在我们真的想要这个序列的部分和,所以让我们计算一下。我们可以编写自己的函数来执行此操作,但这正是Prelude.scanl1的用途:

-- calculates the sequence 0, 0 + 50, 0 + 50 + 100, 0 + 50 + 100 + 150, ... 
let totals = scanl1 (+) ns

现在我们有了总计,唯一真正的工作就是打印它们以及相应的in。所以我们需要i值列表:

let is = [1..10]

现在我们要同时遍历所有三个(nitotal),所以让我们创建一个新列表:

let triples = zip3 ns is totals

现在我们在代码中需要做的就是迭代这些三元组并打印我们的中间结果。在Haskell中,打印定义了IO操作,并且各个IO操作需要在它们运行之前进行组合,这是Control.Monad.forM_所做的,将列表的每个元素上运行给定函数的结果组合成一个巨大的动作

forM_ triples $ \(n,i,total) ->
  -- use show to convert Ints to Strings, and ++ to concatenate Strings
  putStrLn $ "iteration:" ++ show i ++ "\tn:" ++ show n ++ "\ttotal:" ++ show total

或者一起:

module Main where
import Control.Monad (forM_)
main = do
  -- take the first 10 elements from the sequence that starts 0, 50, 100, 150...
  let ns = take 10 [0, 50..]

  -- calculates the sequence 0, 0 + 50, 0 + 50 + 100, 0 + 50 + 100 + 150, ... 
  let totals = scanl1 (+) ns

  let is = [1..10]

  let triples = zip3 ns is totals

  forM_ triples $ \(n,i,total) ->
    -- use show to convert Ints to Strings, and ++ to concatenate Strings
    putStrLn $ "iteration:" ++ show i ++ "\tn:" ++ show n ++ "\ttotal:" ++ show total

它有效!

% ghc SO26540775.hs && ./SO26540775
iteration:1     n:0     total:0
iteration:2     n:50    total:50
iteration:3     n:100   total:150
iteration:4     n:150   total:300
iteration:5     n:200   total:500
iteration:6     n:250   total:750
iteration:7     n:300   total:1050
iteration:8     n:350   total:1400
iteration:9     n:400   total:1800
iteration:10    n:450   total:2250

答案 1 :(得分:2)

将我的代码放在键盘的位置,这是我对@ rampion的答案的修改,使其在重复计数方面更加干燥。

module Main where
import Control.Monad (forM_)
main = do
  -- take the sequence that starts 0, 50, 100, 150...
  let ns = [0, 50..]

  -- and calculate its partial sums:0, 0+50, 0+50+100, 0+50+100+150, ... 
  let totals = scanl1 (+) ns

  -- throw in a list of indexes for counting: 1, 2, 3...
  let is = [1..]

  -- and zip them all together: (1,0,0),(2,50,50),(3,100,150), etc.
  let triples = zip3 is ns totals

  {- Note that so far, all of these sequences are infinitely long.  Haskell
     won't try to calculate any members of them until asked, but it will keep
     calculating as long as you keep asking.  Most languages won't even let you
     define an infinite list, but that's where Haskell's laziness comes in
     handy; the fact that these lists are unbounded does not cause any extra
     work to be performed by the program.

     Still, we only care about the first 10 elements of this list, so let's
     grab those using take (which returns the first n elements of a list for
     some number n) and then iterate over those and print them out: -}

  forM_ (take 10 triples) $ \(i,n,total) ->
    -- use show to convert Ints to Strings, and ++ to concatenate Strings
    putStrLn $ "iteration:" ++ show i ++ "\tn:" ++ show n ++ "\ttotal:" ++ show total