在 F 中使用 Seq.fold 的更好方法#

A better way to use Seq.fold in F#

提问人:Hydraxize 提问时间:3/24/2022 更新时间:3/24/2022 访问量:273

问:

我编写了一个小型控制台应用程序,可以在不使用任何可变变量的情况下更新类型记录。如果对于经验丰富的函数式程序员来说,这看起来很简单,那么对我来说,这是一项相当艰苦的工作。 它有效,但有一件事我不满意。但在此之前,让我们从代码开始:

open System

//------------------------------------------------------------------------------------
// Type, no data validation to keep it simple
//------------------------------------------------------------------------------------
[<StructuredFormatDisplay("{FirstName} {LastName} is a {Age} year old {Sex}")>]
type Student = {
   FirstName: string
   LastName : string
   Sex : char
   Age: int
}


//------------------------------------------------------------------------------------
// I/O functions
//------------------------------------------------------------------------------------
let getConsoleChar message =
   printf "\n%s" message
   Console.ReadKey().KeyChar

let getConsoleString message =
   printf "\n%s" message
   Console.ReadLine()

let getConsoleInt = getConsoleString >> Int32.Parse   //no tryparse to keep it simple, I'm sure you can type an integer

let isValidCommand command = [ 'f'; 'l'; 's'; 'a'; 'x'] |> List.contains command

let isStopCommand = (=) 'x'

let processCommand student command  =
   match command with
   | 'f' -> { student with FirstName =  (getConsoleString "First Name: ")}
   | 'l' -> { student with LastName =  (getConsoleString "Last Name: ")}
   | 's' -> { student with Sex =  (getConsoleChar "Sex: ")}
   | 'a' -> { student with Age =  (getConsoleInt "Age: ")}
   | 'x' -> student
   | _ -> failwith "You've just broken the Internet, theorically you cannot be here"


//------------------------------------------------------------------------------------
// Program
//------------------------------------------------------------------------------------
let initialStudent = {
   FirstName = String.Empty
   LastName = String.Empty
   Sex = Char.MinValue
   Age = 0
   }

let commands = seq {
   while true do 
      yield getConsoleChar "Update [f]irst name, [l]ast name, [s]ex, [a]ge or e[x]it: " }


let finalStudent =
   commands
   |> Seq.filter isValidCommand
   |> Seq.takeWhile (not << isStopCommand)
   |> Seq.map (fun cmd -> (initialStudent, cmd))
   |> Seq.fold (fun student studentAndCommand -> processCommand student (snd studentAndCommand)) initialStudent

printfn "\n<<<< %A >>>>\n" finalStudent

我的问题是

|> Seq.map (fun cmd -> (initialStudent, cmd))
|> Seq.fold (fun student studentAndCommand -> processCommand student (snd studentAndCommand)) initialStudent

将序列 of 转换为 a 以便能够用 a 插入它看起来很奇怪。此外,如果使用 the 作为起点是合乎逻辑的,那么在映射转换中使用它感觉很奇怪(如果将此代码推送到 prod 中,我不确定是否有人会理解逻辑)。charStudent*charSeq.foldinitialStudentSeq.fold

有没有更好的方法来处理命令序列,或者这个代码在函数世界中是标准的和可接受的?

F# 折叠 序列

评论


答:

6赞 Brian Berns 3/24/2022 #1

您可以摆脱并大大简化:mapfold

commands
    |> Seq.filter isValidCommand
    |> Seq.takeWhile (not << isStopCommand)
    |> Seq.fold processCommand initialStudent

我不确定为什么你认为你必须首先将其映射到 a。由于您立即使用从元组中提取元组,撤消映射,因此元组的第一个元素将被完全忽略。更干净,从一开始就避免创建元组seq<char>seq<Student * char>sndchar