【问题标题】:Accessing specific elements in a list of tuples in f# without libraries在没有库的情况下访问 f# 中的元组列表中的特定元素
【发布时间】:2019-03-06 15:55:29
【问题描述】:

我有一个包含三个不同元素的元组列表,如下所示:

[(a0:string, b0:string, c0:int); (a1, b1, c1); (and so on...)]

我需要创建一个函数,该函数接受这个列表和a 形式的“名称”,并给出名称与元组中的 a 匹配的所有 bs 的列表。但我不确定如何迭代和匹配所有内容。

input: function name tuplelist
output: [b0 if a0 = name; b1 if a1 = name; b2 if a2 = name]

我也不能使用库!!!

【问题讨论】:

  • 这个任务需要你自己编写递归列表处理函数。你在课堂或阅读材料中见过任何这样的功能吗?
  • 是的,我知道递归是如何工作的。当涉及到元组中的特定元素时,我只是不确定如何设置它:)

标签: list recursion f# tuples


【解决方案1】:

当以函数式风格编写代码时,您通常会使用递归,如果不是显式的,然后是隐式的,因为大多数列表/数组/序列函数都在底层使用递归。

在 F# 中,您需要明确声明函数是递归的,因此您创建的函数将在其定义中使用 let rec 语法。根据您的要求,您的函数可能如下所示:

let rec myFilter (name: string) (input: (string * string * int) list) =
    ...

对于这类递归迭代列表的问题,您通常使用模式匹配来检查您是否在列表末尾,如果是,则返回一个空列表。

let rec myFilter (name: string) (input: (string * string * int) list) =
        match input with
        | [] -> []
        ...

现在您需要编写一个模式匹配来检查元组中的第一项与提供的名称是否相符。可以在列表头部使用模式匹配,F#的when语法解构列表头部进行比较

let rec myFilter (name: string) (input: (string * string * int) list) =
        match input with
        | [] -> []
        | ((a, b, _) :: rest) when a = name -> b :: myFilter name rest
        ...

a 与查询的名称匹配时,第二种情况匹配。当它匹配时,它将返回一个新列表,其中b 是列表的头部,然后它将获取元组列表的其余部分并递归调用myFilter。这就是递归遍历列表的方式。

我们还有一个案例要检查:如果我们没有找到匹配项,我们希望继续遍历列表而不收集 b。这可以通过剥离头部并递归调用myFilter 来表达,只发送其余的元组。

let rec myFilter (name: string) (input: (string * string * int) list) =
        match input with
        | [] -> []
        | ((a, b, _) :: rest) when a = name -> b :: myFilter name rest
        | (_ :: rest) -> myFilter name rest

调用myFilter "a" [("a","x",3);("b","y",4);("a","z",5)] 然后产生["x"; "z"],正如预期的那样。

【讨论】:

  • 谢谢!尤其是一步一步的解释!
【解决方案2】:

强大的 F# 模式匹配和递归以及类型推断可轻松弥补删除库的限制。

您需要构建一个map函数将一个列表转换为另一个列表,这是通过递归来解决的,应用于列表的每个元素的映射函数可以使用模式匹配将元组分解为组件并执行转换.

如下所示:

let filter name (tupleList:(string*string*int) list) =
    let checkElement name = function | (a,b,c) -> if a = name then Some b else None
    let rec mapList name inputList outputList =
        match inputList with
        | [] -> outputList
        | h::t -> let filter = checkElement name h
                  mapList name t (if filter = None then outputList else (outputList @ [filter.Value]))
    mapList name tupleList [] 

这里的checkElement 是一个映射函数,它接受name 和一个元组(a,b,c),如果a = name,则返回一个选项值Some b,否则返回None。

每个步骤上的递归函数mapList 使用元组的inputList 的未处理部分和outputList 在每个递归步骤上累积匹配元素的部分。在每个递归步骤中,它都会检查 inputList 是否为空。如果是,那么我们就完成了,是时候返回累加结果了,否则我们将头元素从inputList 中分离出来,并对其应用映射函数,如果是这种情况,则更改累加列表。然后我们对inputList的尾部和累加器进行下一个递归步骤。

【讨论】:

    【解决方案3】:

    此任务需要您编写自己的递归列表处理函数。我可以通过使用两个子函数来实现它。

    虽然@GeneBelitski 和@ChadGilbert 的答案非常适合学习,但它们不是尾递归的,所以我会添加自己的。

    子函数aux 接受一个累加器和要处理的列表,并匹配列表的形状。如果为空,则返回到目前为止累积的结果。如果它是一个带尾的三元组,它将第一个元素与name 进行比较,如果它们相等,则继续运行自身,将第二个元素添加到累积的结果和原始列表的尾部,否则只是到目前为止累积的结果,以及原始列表的尾部。

    由于这种累加结果的方式会颠倒结果列表的顺序,所以我们需要在返回之前将其颠倒;这就是子函数rev 所做的事情,如您所见,代码看起来几乎与aux 相同,将元素添加到迄今为止累积的结果中,但没有进行任何比较或处理。

    let filter_b name lst =
        let rec aux acc = function
            | [] -> acc
            | (a : string, b : string, c : int) :: tl ->
                if a = name
                then aux (b :: acc) tl
                else aux acc tl
        let rec rev acc = function
            | [] -> acc
            | hd :: tl -> rev (hd :: acc) tl
        lst
        |> aux []   // process list with aux function
        |> rev []   // reverse resulting list
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-27
      • 2011-08-16
      • 2013-08-17
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多