从Console.In读取的函数返回先前的值

Bor*_*ens 3 f#

为了学习F#我正试图解决Codingame之谜下降

  • 从Console.In.ReadLine()读取8个数字
  • 应打印最高编号的索引
  • 重复8次

下面的代码适用于第一次运行,但似乎在第一次循环后为不同的输入提供完全相同的结果.
使用数组在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. *)
    ()
Run Code Online (Sandbox Code Playgroud)

我的readEightInputs函数有问题吗?

Tay*_*ood 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. *)
    ()
Run Code Online (Sandbox Code Playgroud)

在您的示例中,代码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
  ()
Run Code Online (Sandbox Code Playgroud)

不是将所有函数嵌套在全向函数中,而是将它们提取为通常有用的顶级函数并将它们组合在一起; 并且全功能highestMountain消失了.读取控制台线路并将它们转换为ints 之间也存在分离.如果您仍然想要这个高级函数,现在可以将它构建为其他函数的组合:

// int -> int
let highestMountain = readLines >> Seq.map int >> maxValueIndex
Run Code Online (Sandbox Code Playgroud)

现在这是一个函数,它接受一些(山脉读入)并返回最大的索引,称为like printfn "%i" (highestMountain 8).请注意,虽然highestMountain定义没有明确的args,但它一个函数,因为它是通过>>运算符组成的其他函数.它的参数与第一个函数相同readLines,其返回类型是其最终函数maxValueIndex.这是因为中间(curried)函数Seq.map int与输出readLines和输入兼容maxValueIndex.