String 如何将字符串转换为F中的单词列表

String 如何将字符串转换为F中的单词列表,string,list,f#,String,List,F#,我需要将字符串转换为单词列表 没有内置函数,这里是我到目前为止尝试做的,显然是出了问题: let rec convert word = match word with |"." ->[] |word -> ["word"] |word + " " + words -> [word]@convert words 下面是一种使用递归函数实现的方法,该函数的模式与字符串上的字符列表相匹配: let charsToString chars

我需要将字符串转换为单词列表 没有内置函数,这里是我到目前为止尝试做的,显然是出了问题:

let rec convert word = 
    match word with
    |"."      ->[]
    |word -> ["word"]
    |word + " " + words -> [word]@convert words

下面是一种使用递归函数实现的方法,该函数的模式与字符串上的字符列表相匹配:

let charsToString chars = chars |> Array.ofSeq |> System.String
let split (s: string) =
  let rec loop acc words rest =
    match rest with
    | ' '::xs ->
      if Seq.isEmpty acc then
        loop Seq.empty words xs
      else
        let newWord = charsToString acc
        loop Seq.empty (Seq.append words [newWord]) xs
    | x::xs -> loop (Seq.append acc [x]) words xs
    | [] -> // terminal case, we've reached end of string
      if Seq.isEmpty acc then
        words
      else
        let newWord = charsToString acc
        Seq.append words [newWord]
  loop Seq.empty Seq.empty (List.ofSeq s)

> split "Hello my friend"
val it : seq<System.String> = seq ["Hello"; "my"; "friend"]
在这种情况下使用纯递归的关键是需要跟踪状态:

acc:每当你到达一个空格或整个字符串的末尾时,你积累来构建下一个单词的字符 已经从字符串中拆分出来的单词;每当遇到空格并且acc缓冲区中有字符时,这将改变 rest:字符串中尚未检查的字符;对于每个递归,这将收缩一个字符 这就是内环函数所采用的参数:用于构建单词的字符序列、已拆分的单词序列,以及我们尚未处理的字符串的其余部分。请注意,对循环的第一个调用传递这两个状态的空序列,整个字符串作为一个字符列表作为rest

内环函数仅用于隐藏两个状态值的实现细节,以方便调用者


此实现不是特别高效或优雅,它旨在显示基本的递归和模式匹配概念。

下面是一种使用递归函数实现的方法,该函数的模式匹配字符串作为字符列表:

let charsToString chars = chars |> Array.ofSeq |> System.String
let split (s: string) =
  let rec loop acc words rest =
    match rest with
    | ' '::xs ->
      if Seq.isEmpty acc then
        loop Seq.empty words xs
      else
        let newWord = charsToString acc
        loop Seq.empty (Seq.append words [newWord]) xs
    | x::xs -> loop (Seq.append acc [x]) words xs
    | [] -> // terminal case, we've reached end of string
      if Seq.isEmpty acc then
        words
      else
        let newWord = charsToString acc
        Seq.append words [newWord]
  loop Seq.empty Seq.empty (List.ofSeq s)

> split "Hello my friend"
val it : seq<System.String> = seq ["Hello"; "my"; "friend"]
在这种情况下使用纯递归的关键是需要跟踪状态:

acc:每当你到达一个空格或整个字符串的末尾时,你积累来构建下一个单词的字符 已经从字符串中拆分出来的单词;每当遇到空格并且acc缓冲区中有字符时,这将改变 rest:字符串中尚未检查的字符;对于每个递归,这将收缩一个字符 这就是内环函数所采用的参数:用于构建单词的字符序列、已拆分的单词序列,以及我们尚未处理的字符串的其余部分。请注意,对循环的第一个调用传递这两个状态的空序列,整个字符串作为一个字符列表作为rest

内环函数仅用于隐藏两个状态值的实现细节,以方便调用者

此实现不是特别高效或优雅,它旨在显示基本的递归和模式匹配概念。

试试这个:

let rec words word text =
  [ match text with
    | [] -> yield word
    | c :: tail ->
        match c with
        | ' ' -> yield word
                 yield! words "" tail
        | _ -> yield! words (sprintf "%s%c" word c) tail ]


printfn "%A" ("hello my friend"
              |> Seq.toList
              |> words "")

["hello"; "my"; "friend"]
这很简单,但不是很有效…

试试这个:

let rec words word text =
  [ match text with
    | [] -> yield word
    | c :: tail ->
        match c with
        | ' ' -> yield word
                 yield! words "" tail
        | _ -> yield! words (sprintf "%s%c" word c) tail ]


printfn "%A" ("hello my friend"
              |> Seq.toList
              |> words "")

["hello"; "my"; "friend"]

这很简单,但不是很有效…

因为你的问题有点学术味道,我将分别探讨解决方案

暂且不考虑不使用内置库的要求,该解决方案可能会遵循经典模式,该模式很容易从头开始实现,假设稍后将实现某些拆分属性:

let string2Words s =
    let rec fold acc s =
        match split s with
        | [x] -> acc @ [x] // append the last word; done
        | [head;tail] -> fold (acc @ [head]) tail // append the head word; continue splitting
        | _ -> acc // done
    fold [] s
因此,我们的任务现在减少到实现这样的拆分,即接受一个字符串,该字符串返回一个包含单个单词的列表,或包含两个元素的列表,其中包含一个首单词和字符串的其余部分,或者任何表示没有其他内容可供进一步拆分的信号,以及传递结果的时间

利用字符串作为char[]的二元性,我们现在可以依靠字符串索引器和切片而不是F库来实现拆分:

let split s =
    let rec scan (s:string) i =
        if s.Length = 0 then []
        elif s.[i] = ' ' && i = 0 then scan s.[i+1..] 0
        elif s.[i] = ' ' then [s.[..i-1]; s.[i+1..]]
        elif i = (s.Length - 1) then [s]
        else scan s (i+1)
    scan s 0
内部递归扫描函数完成了我们的折叠所期望的工作,滥用字符串切片和索引器,并在处理过程中处理角案例

现在把所有的东西放在一起

let string2Words s =
    let split s =
        let rec scan (s:string) i =
            if s.Length = 0 then []
            elif s.[i] = ' ' && i = 0 then scan s.[i+1..] 0
            elif s.[i] = ' ' then [s.[..i-1]; s.[i+1..]]
            elif i = (s.Length - 1) then [s]
            else scan s (i+1)
        scan s 0
    let rec fold acc s =
        match split s with
        | [x] -> acc @ [x]
        | [head;tail] -> fold (acc @ [head]) tail
        | _ -> acc
    fold [] s
和快速检查fsi:

> string2Words "life without libraries is tough";;
val it : string list = ["life"; "without"; "libraries"; "is"; "tough"]

由于你的问题有点学术色彩,我将分别讨论解决方案

暂且不考虑不使用内置库的要求,该解决方案可能会遵循经典模式,该模式很容易从头开始实现,假设稍后将实现某些拆分属性:

let string2Words s =
    let rec fold acc s =
        match split s with
        | [x] -> acc @ [x] // append the last word; done
        | [head;tail] -> fold (acc @ [head]) tail // append the head word; continue splitting
        | _ -> acc // done
    fold [] s
因此,我们的任务现在减少到实现这样的拆分,即接受一个字符串,该字符串返回一个包含单个单词的列表,或包含两个元素的列表,其中包含一个首单词和字符串的其余部分,或者任何表示没有其他内容可供进一步拆分的信号,以及传递结果的时间

利用字符串作为char[]的二元性,我们现在可以依靠字符串索引器和切片而不是F库来实现拆分:

let split s =
    let rec scan (s:string) i =
        if s.Length = 0 then []
        elif s.[i] = ' ' && i = 0 then scan s.[i+1..] 0
        elif s.[i] = ' ' then [s.[..i-1]; s.[i+1..]]
        elif i = (s.Length - 1) then [s]
        else scan s (i+1)
    scan s 0
内部递归扫描函数完成了我们的折叠所期望的工作,滥用字符串切片和索引器,并在处理过程中处理角案例

现在把所有的东西放在一起

let string2Words s =
    let split s =
        let rec scan (s:string) i =
            if s.Length = 0 then []
            elif s.[i] = ' ' && i = 0 then scan s.[i+1..] 0
            elif s.[i] = ' ' then [s.[..i-1]; s.[i+1..]]
            elif i = (s.Length - 1) then [s]
            else scan s (i+1)
        scan s 0
    let rec fold acc s =
        match split s with
        | [x] -> acc @ [x]
        | [head;tail] -> fold (acc @ [head]) tail
        | _ -> acc
    fold [] s
和快速检查fsi:

> string2Words "life without libraries is tough";;
val it : string list = ["life"; "without"; "libraries"; "is"; "tough"]

为了理解递归,您必须首先理解递归。但是你可以离开裸露的金属,做一些线性管道。链接预定义的库函数,每个函数在一系列中进行一次转换以获得所需的结果

"Hello, World ! "
|> fun s ->
    (s.ToCharArray(), ([], []))
    ||> Array.foldBack (fun c (cs, css) ->
        if c = ' ' then [], cs::css else c::cs, css )
|> List.Cons
|> List.filter (not << List.isEmpty)
|> List.map (fun s -> System.String(Array.ofList s))
// val it : System.String list = ["Hello,"; "World"; "!"]
我们将字符串转换为ch
aracter array char[]并对数组的每个元素应用一个文件夹,其中包含一个char list(当前单词的字符)和char list(到目前为止的单词)的元组。这是按照从后到前的相反顺序完成的,以正确的顺序构造元组列表。此步骤的结果是生成一个单独的字符列表,过滤空列表,并重新组合为字符串。

为了理解递归,必须首先理解递归。但是你可以离开裸露的金属,做一些线性管道。链接预定义的库函数,每个函数在一系列中进行一次转换以获得所需的结果

"Hello, World ! "
|> fun s ->
    (s.ToCharArray(), ([], []))
    ||> Array.foldBack (fun c (cs, css) ->
        if c = ' ' then [], cs::css else c::cs, css )
|> List.Cons
|> List.filter (not << List.isEmpty)
|> List.map (fun s -> System.String(Array.ofList s))
// val it : System.String list = ["Hello,"; "World"; "!"]

我们将字符串转换为字符数组char[],并对数组的每个元素应用一个文件夹,其中包含一个char list(当前单词的字符)和char list(到目前为止的单词)的元组。这是按照从后到前的相反顺序完成的,以正确的顺序构造元组列表。这一步的结果是生成一个单独的字符列表,过滤空列表,然后重新组合成字符串。

您必须实现拆分函数,对吗?没有雾,我必须使用递归函数解决它。您可以显示输入和预期输出的示例吗?输入:Hello my friendoutput:[Hello;my;friend]您必须实现拆分函数,对吗?没有问题,我必须使用递归函数来解决它。您可以显示输入和预期输出的示例吗?输入:Hello my friendoutput:[Hello;my;friend]如果您使用类似Hello world的字符串,这将不会给您带来好的结果。结果是[hello;;;world]。如果想要避免在结果中出现空字符串,那么在|“”->yield word中,只有当word不为空时才应该使用该yield word。如果您有像hello world这样的字符串,这不会给您带来好的结果。结果是[hello;;;world]。如果要避免结果中出现空字符串,则在|“”->yield word中,仅当word不为空时才应使用该yield word。请求是一种不基于内置函数的解决方案。请求是一种不基于内置函数的解决方案。