在解决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
绑定已被遮盖。但是,如果不涉及阴影,则会给出错误的输出。
答案 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