调试 Seq.sumBy

Debugging Seq.sumBy

我试图通过查看去年的 AdventOfCode 解决方案来学习一些 F#。我遇到了这个 neat peice of code,我根本无法解析它:

i 1|>Seq.sumBy(" (".IndexOf)

注意,我相信我理解前面的行(在link):

let i n=System.IO.File.ReadAllText(sprintf "%s/input/input%d.txt"__SOURCE_DIRECTORY__ n)

它创建了一个函数 i,它接受一个 integer n 并将文件 inputN.txt 和 returns 作为字符串读取。因此 i 1 returns input1.txt 作为字符串。

然后 |> 只是将字符串(或字符数组?)作为第一个参数传递给下一个函数,即 Seq.sumBy

但随后事情开始崩溃...

sumBy 似乎很直接:

Returns the sum of the results generated by applying the function to each element of the list.

但是字符串 " ("IndexOf 让我感到困惑。

现在,我真的不想要任何鱼,我想知道的是这个。作为这门外语的新手,随着我学习更多 F# 的工作,我如何才能将这段代码分解成更小的部分来测试它以弄清楚发生了什么?令我抓狂的是我有解决方案,有 google/so,但我仍然无法理解这段代码。

谁能告诉我一些更小的片段,以便我自己找到答案?

因此,我们可以将其分解。

i 1|>Seq.sumBy(" (".IndexOf)

您对 i 1 部分的看法是正确的。这将显示 input1.txt,并以 string.

的形式为您提供整个文本

所以,这里的第一个键是String implements IEnumerable<char>char seq),也就是说它是可以枚举的东西。

接下来,让我们看一下parens里面的部分:

" (".IndexOf

第一部分只是一个字符串:" ("IndexOf是字符串的一个方法。它 return 是特定字符的从零开始的索引,如果不存在则为 -1。

因为它是一种方法,所以您可以将它用作函数 - 所以 " (".IndexOf 可以被认为是这样的:

(fun someChar -> 
              let str = " ("
              str.IndexOf(someChar))

-------- 除非你想要详细解释的完整答案,否则就停在这里--------

在这种情况下,如果输入的字符是' ',则return0,如果是'(',则return1,如果是其他的,它会 return -1.

Seq.sumBy 获取输入字符串的每个字符并将其通过管道传递给此函数,然后对结果求和。这意味着每个输入 '(' 将加 1,每个输入 ' ' 将加 0,其他任何内容将添加 -1(在本例中为 ')' 个字符。像这样的字符串 "()" 将加 1,然后加 -1,结果是 0,这与 goal of the day 1 advent challenge.

相匹配

FSI 是您的朋友。我经常用它来理解如何分解函数。如果将表达式 " (".IndexOf 粘贴到 FSI 中,乍一看似乎没有帮助:

> " (".IndexOf;;

  " (".IndexOf;;
  ^^^^^^^^^^^^

stdin(12,1): error FS0041: A unique overload for method 'IndexOf' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: System.String.IndexOf(value: char) : int, System.String.IndexOf(value: string) : int

如您所知," (" 是一个字符串,IndexOfstring 上的一个方法。事实上,该方法有 quite a few overloads 个,但只有两个 arity 1.

其中一个重载以 char 作为输入,另一个以 string 作为输入。

表达式" (".IndexOf如果是一个函数。这是 fun x -> " (".IndexOf x.

的缩写形式

您还已经确定 string 实现了 char seq,因此当您在其上使用 Seq 模块时,您正在查看序列的每个元素。在这种情况下,每个元素都是一个 char,因此这里使用的重载必须是将 char 作为输入的重载。

现在您已经确定了正在使用的重载,您可以开始在 FSI 中进行试验:

> " (".IndexOf '(';;
val it : int = 1
> " (".IndexOf 'f';;
val it : int = -1
> " (".IndexOf 'o';;
val it : int = -1
> " (".IndexOf ' ';;
val it : int = 0

显然,该函数在" ("中查找每个输入char的索引,因此每次传入'('时都会得到1(因为它是零-indexed),当输入为 ' ' 时,return 的值为 0。对于所有其他值,return 值为 -1

"(foo bar)" 这样的输入字符串也是 char seq。您可以尝试将其输入 Seq.map,而不是执行 sumBy,以了解每个元素是如何被翻译的:

> "(foo bar)" |> Seq.map (" (".IndexOf) |> Seq.toList;;
val it : int list = [1; -1; -1; -1; 0; -1; -1; -1; -1]

现在,Seq.map 只翻译,但 Seq.sumBy 将所有这些数字加在一起:

> "(foo bar)" |> Seq.sumBy (" (".IndexOf);;
val it : int = -6

我仍然猜不出目的是什么,但是,我从来没有见过输入字符串...