为什么这三个示例给出不同的结果?他们似乎是等效的

时间:2018-12-01 06:01:42

标签: haskell

在解决AdventOfCode 2018第1天问题2的过程中,我遇到了一些奇怪的行为。以下代码可以正常工作,并打印出正确的解决方案。

day1_2 = do
    content <- day1
    let content = scanl (+) 0 content
    return $ dup content
  where
    dup xs = dup' xs []
      where
        dup' [] _ = Nothing
        dup' (x : xs) seen =
            if x `elem` seen then Just x else dup' xs (x : seen)

它可以工作,但是我想稍微清理一下。删除content的第二个绑定并内联函数调用后,代码立即停止工作。由于dup函数不变,因此我在其余的代码段中都省略了它。

day1_2 = do
    content <- day1
    return $ dup $ scanl (+) 0 content

我对Haskell的理解告诉我,这两个函数应该给出相同的结果,但事实并非如此。在输入中,第一个函数返回Just 0,第二个函数返回Nothing。这是一个错误,还是我缺少什么?

进一步减少代码仍然可以避免第二个函数的行为。

day1_2 = dup . scanl (+) 0 <$> day1

这里day1仅读取第1天给出的输入,并使其对Haskell可以解析。

day1 =
    map (read . (\n -> if head n == '+' then tail n else n))
        .   words
        <$> readFile "input/day1.txt"

更奇怪:

day1_2 = do
    content <- day1
    let content' = scanl (+) 0 content
    return $ dup content

以上内容给了我Just 6。 (不是预期的结果,而是预期的结果)

day1_2 = do
    content <- day1
    let smth = scanl (+) 0 content
    return $ dup smth

但是,尽管在功能上与第一个代码段相同,但以上内容为我提供了Nothing,但在命名上有所不同。从这些最后的测试中可以明显看出,原始文档中的content绑定已被遮盖。但是,如果不涉及阴影,则会给出错误的输出。

My specific input for day1.

2 个答案:

答案 0 :(得分:1)

let content = scanl (+) 0 content时,第二个content不是上一行声明的content,而是该行声明的content使用let

因此,第一个代码仅忽略从content获得的day1,但是第二个代码使用它。这就是为什么这两个行为不同的原因。

答案 1 :(得分:1)

您的原始代码已损坏。

do content <- day1
   let content = scanl (+) 0 content
   _etc

不是指“将day1的结果绑定到content,对其应用scanl (+) 0,然后将其绑定到content”。实际上,您编写的内容与此相同:

do day1
   let content = scanl (+) 0 content
   _etc

您要执行day1,丢弃其结果,然后绑定到content的递归定义列表。您是正确的,content被遮盖了,但是您没有意识到第二个content在其自己的定义中遮盖了第一个。如果您输入let content = scanl (+) 0 content in content,实际上您会看到0的无限列表。很明显为什么您会随后获得Just 0作为“答案”。

“正确”的解决方案实际上是最后的解决方案。

day1_2 = do content <- day1
            let smth = scanl (+) 0 content
            return $ dup smth
-- don't need do notation; just functor-function-application
day1_2 = dup <$> scanl (+) 0 <$> day1

但是您错过了question的一个关键部分:

  

请注意,您的设备可能需要多次重复其频率变化列表,然后才能找到重复的频率

您只需调用一个标准库函数即可完成此工作。看看是否可以找到它:)。 (警告:找到并使用该功能后,此解决方案将花费几分钟的时间才能完成运行,但确实可以使用。)

day1_2 = dup <$> scanl (+) 0 <$> _libfunction <$> day1