计数器每次初始化? [英] Counters are initialized every time?

查看:112
本文介绍了计数器每次初始化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图做一个简单的计数器。然而,我的柜台不会上涨。在我看来,似乎每次都通过函数inc重新初始化,或者(n + 1)不起作用。如何最好地解决这个问题?

  inc :: Int  - > IO int 
inc n = return(n + 1)

main :: IO()
main = do
let c = 0
let f = 0
putStrLnStarting ...
conn< - connect192.168.35.628081
time $
forM_ [0..10000] $ \i - >做
p < - ping connping
if p ==pongthen inc c
else inc f
printfRoundtrips%d \\\
(c: :Int)


解决方案尽管可变变量可以在Haskell中使用如其他评论者所示,这不是一种好的风格:在大多数情况下不应该使用突变。

$ b $ inc 函数通过值接受它的参数,也就是说,它不修改它的参数。另外,由 let 声明的变量保持它们的初始值,所以你不能改变它们。



你怎么写如果没有变量可以改变?答案是:


  1. 代替原地修改,返回新值

  2. 循环,使用递归

幸运的是,您很少需要自己编写递归,因为大多数递归模式已经存在于标准库中。 / p>

在您的情况下,您需要执行多个IO操作并返回两个计数器的最终值。让我们从一个动作开始:

pre $ let tryOnePing(c,f)i = do
p < - ping conn ping
return $ if p ==pongthen(c + 1,f)else(c,f + 1)

这里我们声明一个带有2个参数的本地函数:计数器的当前值,打包在一个元组中(Int,Int)(其他语言的结构)和当前迭代 Int 。该函数执行IO操作并返回计数器 IO(Int,Int)的修改值。这一切都以其类型表示:

  tryOnePing ::(Int,Int) - > Int  - > IO(Int,Int)

ping 返回值为 IO字符串类型。为了比较它,你需要一个字符串而没有 IO 。为此,您应该使用>> = 函数:

  let tryOnePing(c,f)i = ping connping>> = \ p  - > {以某种方式处理字符串} 

由于这种模式很常见,所以可以这样写

  let tryOnePing(c,f)i = do 
p < - ping connping
{字符串以某种方式}

但是其含义完全相同(编译器翻译 do 符号到>> = 的应用程序中)。

更常见的模式:

pre $ if p ==pongthen(c + 1,f)else(c,f + 1 )

这里 if code> if 但更像是一个三元条件? value1:value2 其他语言的运算符。另请注意,我们的 tryOnePing 函数接受(c,f)并返回(c + 1,f)(c,f + 1)。我们使用元组,因为我们只需要使用2个计数器。在大量计数器的情况下,我们需要声明一个结构类型并使用命名字段。

整个If构造的值是一个元组(Int,Int )。 ping 是IO操作,所以 tryOnePing 也必须是IO操作。 return 函数不是强制性返回,而是将(Int,Int)转换为 IO(Int,Int)



所以,我们有tryOnePing,我们需要编写一个循环来运行1000次。您的 forM _ 不是一个好选择:


  1. 迭代之间的计数器

  2. _ 表示它抛出计数器的最终值而不是返回它

您需要的不是 forM _ ,而是 foldM

  foldM tryOnePing(0,0)[0 .. 10000] 

foldM 执行IO操作,通过列表中的每个元素进行参数化,并在迭代之间传递一些状态,在我们的例子中两个柜台。它接受初始状态,并返回最终状态。当然,当它执行IO操作时,它会返回IO(Int,Int),所以我们需要使用>> = 来再次提取它以显示: (0,0)[0..10000]>> = \(c,f) - >>< b
$ b

  foldM tryOnePing print(c,f)

在Haskell中,您可以执行所谓的'eta reduction'您可以从函数声明的两侧删除相同的标识符。例如。 \foo - > bar foo bar 相同。因此,在这种情况下,使用>> = 您可以这样写:

  foldM tryOnePing(0,0)[0..10000]>> =打印

其中 do 符号:

  do 
( c,f)< - foldM tryOnePing(0,0)[0..10000]
print(c,f)

另外请注意,你不需要有两个计数器:如果你有3000次成功,那么你有7000次失败。所以代码变成:

  main = do 
conn < - connect192.168.35.628081
让tryOnePing ci = do
p < - ping connping
return $ if p ==pongthen c + 1 else c
c < - foldM tryOnePing 0 [0。 。10000]
print(c,10000 - c)

最后,在Haskell中它的优点将IO操作与非IO代码分开。因此,最好将所有ping结果收集到一个列表中,然后对其中的ping进行计数:

  main = do 
conn< - connect192.168.35.628081
let tryOnePing i = ping connping
pings< - mapM tryOnePing [0 .. 10000]
let c = length $ filter(\ ping - > ping ==pong)ping
print(c,10000 -c)

注意我们完全避免了递增。

它可以写得更短,但需要更多的读写技巧。不用担心,您将很快了解这些技巧:

  main = do 
conn< - connect192.168 .35.628081
c <-fmap(length。filter(==pong))$ mapM(const $ ping connping)[0..10000]
print(c,10000 - c)


I try to make a simple counter. My counters do not go up however. It seems to me as if they are re-initialized every time by the function "inc" or maybe the (n+1) does not work. How do I best fix this?

inc :: Int -> IO Int
inc n = return (n+1)

main :: IO ()
main = do
  let c = 0
  let f = 0
  putStrLn "Starting..."
  conn <- connect "192.168.35.62" 8081
  time $
    forM_ [0..10000] $ \i -> do
      p <- ping conn "ping"
      if p=="pong" then inc c
         else inc f
  printf "Roundtrips %d\n" (c::Int)

解决方案

Though mutable variables can be used in Haskell as shown by other commenters, it is not a good style: mutation should not be used in most cases.

The inc function accepts its argument by value, that is, it doesn't modify its argument. Also, the variables declared by let keep their initial values, so you cannot change them.

How do you write if no variable can ever be changed? The answer is:

  1. instead of modifying something in-place, return a new value
  2. for loops, use recursion

Fortunately, you rarely need to write recursion yourself, as most of recursive patterns are already in the standard library.

In your case you need to perform several IO actions and return the final value of the two counters. Let's start from one action:

let tryOnePing (c, f) i = do
    p <- ping conn "ping"
    return $ if p == "pong" then (c+1, f) else (c, f+1)

Here we declare a local function with 2 parameters: the current values of the counters, packed in a tuple (Int, Int) (a structure in other languages) and current iteration Int. The function performs IO actions and returns modified values of the counters IO (Int, Int). This all is indicated in its type:

tryOnePing :: (Int, Int) -> Int -> IO (Int, Int)

ping returns a value of IO String type. To compare it, you need a String without IO. To do that, you should use >>= function:

let tryOnePing (c, f) i = ping conn "ping" >>= \p -> {process the string somehow}

As this pattern is common, it can be written like this

let tryOnePing (c, f) i = do
    p <- ping conn "ping"
    {process the string somehow}

But the meaning is exactly the same (compiler translates do notation into applications of >>=).

The processing shows a few more common patterns:

if p == "pong" then (c+1, f) else (c, f+1)

Here if is not an imperative if but more like a ternary condition ? value1 : value2 operator in other languages. Also note, that our tryOnePing function accepts (c, f) and returns either (c+1, f) or (c, f+1). We used tuples as we need to work only with 2 counters. In case of big number of counters, we would need to declare a structure type and use named fields.

The value of the whole If construct is a tuple (Int, Int). ping is an IO action, so tryOnePing must be an IO action too. The return function is not an imperative return but a way to convert (Int, Int) to IO (Int, Int).

So, as we have tryOnePing, we need to write a loop to run it 1000 times. Your forM_ was not a good choice:

  1. It doesn't pass our two counters between iterations
  2. _ indicates that it throws the final value of the counters away instead of returning it

You need here not forM_ but foldM

foldM tryOnePing (0, 0) [0 .. 10000]

foldM performs an IO action parametrized by each element of the list and passes some state between iterations, in our case the two counters. It accepts the initial state, and returns the final state. Of course, as its performs IO actions, it returns IO (Int, Int), so we need to use >>= to extract it again for displaying:

foldM tryOnePing (0, 0) [0 .. 10000] >>= \(c, f) -> print (c, f)

In Haskell, you can perform so called 'eta reductions', that is you can remove same identifiers from both sides of a function declaration. E.g. \foo -> bar foo is the same as just bar. So in this case with >>= you can write:

foldM tryOnePing (0, 0) [0 .. 10000] >>= print

which is much shorter than do notation:

 do
   (c, f) <- foldM tryOnePing (0, 0) [0 .. 10000]
   print (c, f)

Also note that you don't need to have two counters: if you have 3000 successes then you have 7000 failures. So the code becomes:

main = do
    conn <- connect "192.168.35.62" 8081
    let tryOnePing c i = do
        p <- ping conn "ping"
        return $ if p == "pong" then c+1 else c
    c <- foldM tryOnePing 0 [0 .. 10000]
    print (c, 10000 - c)

Finally, in Haskell its good to separate IO actions from non-IO code. So it's better to collect all results from pings into a list and then to count successful pings in it:

main = do
    conn <- connect "192.168.35.62" 8081
    let tryOnePing i = ping conn "ping"
    pings <- mapM tryOnePing [0 .. 10000]
    let c = length $ filter (\ping -> ping == "pong") pings
    print (c, 10000 - c)

Note that we avoided incrementing altogether.

It can be written even shorter, but requires more skill to read and write. Don't worry, you will learn these tricks soon:

main = do
    conn <- connect "192.168.35.62" 8081
    c <- fmap (length . filter (== "pong")) $ mapM (const $ ping conn "ping") [0 .. 10000]
    print (c, 10000 - c)

这篇关于计数器每次初始化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆