我刚刚开始学习F#,并且为了提高自己的技能,我正在尝试2019 Advent of Code。我正在尝试第2天的第一个难题,但已陷入困境。请参阅底部第2天的规则。
我认为我遇到的问题是我不太了解编译器如何计算递归函数的返回类型。在这里查看我的尝试:
let rec intcode (arr: int []) op = // int [] -> int -> int []
let in1 = op + 1
let in2 = op + 2
let out = op + 3
match arr.[op] with
| 1 ->
arr.[arr.[out]] <- arr.[arr.[in1]] + arr.[arr.[in2]]
intcode arr op+4
| 2 ->
arr.[arr.[out]] <- arr.[arr.[in1]] * arr.[arr.[in2]]
intcode arr op+4
| 99 ->
arr
| _ ->
printfn "Had input operator that was not 1, 2, or 99"
arr
编译器为我提供了在函数int [] -> int -> int []
开头注释的函数签名。
但是,编译器还告诉我,在匹配模式arr
,99
时尝试返回error FS0001: The type 'int' does not match the type 'int []'
时。
是不是告诉我它认为arr
是int
?还是认为该函数的输出应该是int
?
规则
Intcode程序是由逗号分隔的整数列表(例如1,0,0,3,99)。要运行一个,首先查看第一个整数(称为位置0)。在这里,您会找到一个操作码-1、2或99。例如,99表示程序已完成,应立即停止。遇到未知的操作码意味着出现了问题。
操作码1将两个位置读取的数字相加,并将结果存储在第三位置。操作码后面的三个整数告诉您这三个位置-前两个指示您应从中读取输入值的位置,第三个指示应将输出存储在其中的位置。
例如,如果您的Intcode计算机遇到1,10,20,30,则应读取位置10和20的值,将这些值相加,然后用它们的和覆盖位置30的值。
操作码2的工作原理与操作码1完全相同,只是它会将两个输入相乘而不是相加。同样,操作码后面的三个整数表示输入和输出的位置,而不是它们的值。
处理完一个操作码后,向前移4个位置即可移至下一个。
答案 0 :(得分:2)
问题不是(直接)与类型推断有关,而是与operator precedence有关。
intcode arr op+4
解析为
(intcode arr op)+4
不是
intcode arr (op+4)
因为函数应用程序的优先级高于+
运算符。
根据前者的分组,编译器推断表达式必须为int
类型,因为最外面的表达式是int
的加法。
解决此问题所需要做的就是在op+4
前后加上括号。
答案 1 :(得分:1)
这不是问题的答案,而是原始问题的答案。 我以为我会将其重写为一个不变的版本。我希望它很容易阅读。
let run =
let rec runr bp memory =
let read pos = memory |> Array.item (bp + pos)
let read' pos = memory |> Array.item (read pos)
let write pos value = memory |> Array.mapi (fun i oldv -> if i = pos then value else oldv)
let exec op = op (read' 1) (read' 2) |> write (read 3)
let apply op = runr (bp + 4) (exec op)
match read 0 with
| 1 -> apply (+)
| 2 -> apply (*)
| 99 -> memory
| _ -> failwith "Invalid op code"
runr 0
let result = run [|1;0;0;3;99|]
printf "%A" result