为了学习F#我正试图解决Codingame之谜The Descent
下面的代码适用于第一次运行,但似乎在第一次循环后为不同的输入提供完全相同的结果 使用数组在Interactive中运行idxWithHighestValue函数而不是从控制台读取,可以得到所需的结果。
预期行为:
[9,8,7,6,5,4,3,2] => 0
[0,8,7,6,5,4,3,2] => 1
实际行为:
[9,8,7,6,5,4,3,2] => 0
[0,8,7,6,5,4,3,2] => 0
open System
let highestMountain =
let readEightInputs =
let readlines n = Array.init n (fun _ -> Console.In.ReadLine() |> int)
readlines 8
let idxWithHighestValue list =
list
|> Seq.mapi (fun i v -> i, v)
|> Seq.maxBy snd
|> fst
idxWithHighestValue readEightInputs
(* game loop *)
while true do
(* Write an action using printfn *)
(* To debug: Console.Error.WriteLine("Debug message") *)
printfn "%i" highestMountain(* The index of the mountain to fire on. *)
()
我的readEightInputs函数有问题吗?
答案 0 :(得分:6)
问题是highestMountain
只是int
值,但您希望它是函数,它读取控制台输入并且返回 int
即其类型签名应为unit -> int
,而不仅仅是int
。简单地将()
(单位)添加到声明中使其成为可以被调用的函数 - 而不仅仅是要计算一次的值。然后你可以在循环中调用该函数:
let highestMountain () =
let readEightInputs =
let readlines n = Array.init n (fun _ -> Console.In.ReadLine() |> int)
readlines 8
let idxWithHighestValue list =
list
|> Seq.mapi (fun i v -> i, v)
|> Seq.maxBy snd
|> fst
idxWithHighestValue readEightInputs
while true do
let result = highestMountain()
printfn "%i" result (* The index of the mountain to fire on. *)
()
在您的示例中,highestMountain
中的代码仅一次进行评估,以获得其最终int
值,并且无需再次对其进行评估;就F#而言,它的价值永远不会改变。使其成为功能意味着您可以根据需要多次调用它来获得不同的输入。
这是通过提取通常有用的函数来重构此代码的一种方法:
let readLines count = // int -> string[]
[| for _ in 1 .. count -> Console.ReadLine() |]
let maxValueIndex source = // seq<'a> -> int
source
|> Seq.mapi (fun i v -> i, v)
|> Seq.maxBy snd
|> fst
while true do
let highMountainIdx = readLines 8 |> Seq.map int |> maxValueIndex
printfn "%i" highMountainIdx
()
不是将所有函数嵌套在泛函数中,而是将它们提取为通常有用的顶级函数并将它们组合在一起;并且全向函数highestMountain
消失了。在阅读控制台线路并将它们转换为int
之间也存在分离关注点。如果您仍然想要这个高级功能,现在可以将其构建为其他功能的组合:
// int -> int
let highestMountain = readLines >> Seq.map int >> maxValueIndex
现在这是一个函数,它接受一些(山脉读入)并返回最大的索引,称为printfn "%i" (highestMountain 8)
。请注意,虽然highestMountain
定义没有明确的args,但 是一个函数,因为它是>>
运算符组成的其他函数。它的参数与第一个函数readLines
相同,其返回类型是其最终函数maxValueIndex
。这是有效的,因为中间(curried)函数Seq.map int
与readLines
的输出和maxValueIndex
的输入兼容。
答案 1 :(得分:3)
Taylor Wood已经指出highestMountain
只是一个整数值,而不是一个函数。我只想提一下你问“我的readEightInputs函数有什么问题吗?”,但实际上在你向我们展示的代码中,readEightInputs
也是一个值,不是一个功能。在F#中,这是一个值:
let name = (series of expressions)
这是一个功能:
let name parameter = (series of expressions)
如果名称后面至少有一个参数,即使该参数是“单位”值()
,那么它也是一个函数,每次调用时都会评估=字符后的一系列表达式功能。如果没有参数,那么它只是一次评估的值。 (顺便说一下,“单位”值()
对于了解非常有用。可以将此值视为空元组:空元组只有一个可能的值,因此类型称为unit
,因为只能有一个)。
我还要提到的另一件事:你的readEightInputs
值,即使你把它变成一个函数,看起来有点傻。为什么不直接使用您的readlines
功能? readlines
函数是一个很好的,有用的函数。您可以将该行写为idxWithHighestValue readEightInputs
,而不是idxWithHighestValue (readlines 8)
。 Voila:现在如果事实证明你必须阅读10行以便以后练习,你可以拨打readlines 10
。代码重用是一件好事。