【问题标题】:Product of 2 Lists in ocaml without Imperative Functionsocaml 中没有命令式函数的 2 个列表的乘积
【发布时间】:2018-09-17 15:54:53
【问题描述】:

人,

我正在尝试通过 ocaml 和 CYK 表来学习函数式编程,因此没有 List.mem 或任何命令式函数。我的目标是形成 2 个细胞的产物。

这是我目前拥有的:

let stringlister = function(mystring, newlist) ->
List.append newlist mystring;;

let rec append_func = function([listleft;listright], anslist, i, j) ->
if (j == (List.length listright)) then anslist
else begin
     append_func([listleft;listright], anslist, i, j + 1);
     List.append(anslist (stringlister((List.nth listright j), (stringlister( (List.nth listleft i), [])))))

   end;;

let rec prod_func = function([listleft;listright], anslist, i, j) ->
if (i == (List.length listleft)) then anslist
else begin
     prod_func([listleft;listright], anslist, i + 1, j);
     append_func([listleft;listright], anslist, i, j)
   end;;

let product = function[listleft;listright] ->
if (listleft == [] || listright == []) then []
else prod_func([listleft;listright], [], 0, 0);;

预期的输出应该是这样的:

#product[["A";"B"];["D","E","F","G"]];;
-: string list = ["AD"; "AE"; "AF"; "AG"; "BD"; "BE"; "BF"; "BG"]

#product[["A","B"];[]];;
-: string list = []

我的想法是制作一系列递归函数,基本上循环遍历列表,以将每个字符串与另一个列表中的每个字符串放在一起。

我认为我的错误在于我的附加方式,特别是在 append_func 中。我认为更好的问题可能是如何创建字符串列表。

【问题讨论】:

  • 您不应该使用== 进行相等比较,而应使用更简单的= 运算符。在这种特定情况下,它会起作用,但是当您将它用于更复杂的数据时,您会得到令人不快的惊喜。

标签: functional-programming ocaml cyk


【解决方案1】:

想象一个用于嵌套循环的 C。 此外,这个想法是循环遍历第二个列表,从尾部开始。将它放在第一个列表的另一个循环中,从尾部开始。第一轮将到达两个列表的末尾,您希望它返回一个空列表。然后它将开始回溯两个列表的最后一个元素。您要返回的元素是第一个列表头与第二个列表头连接。这将转到您刚刚创建的同一个列表。它以尾部开头的原因是因为列表是不可变的,只需在列表前面添加一个新的头部就可以减少消耗。您的函数有一个参数和两个列表。但是,这不是您想要的列表,而是列表中的内容,它位于箭头的左侧,即两个列表的头部和尾部。现在请记住,您正在循环遍历第二个列表,循环遍历第一个列表,然后以相反的顺序连接头部。

【讨论】:

    【解决方案2】:

    使用 Monad (monads for functionnal programming) 可以简化您的代码。

    module ListMonad =
    struct
      type 'a t = 'a list
      let return x = [x]                                                        
      let bind l f = List.fold_right (fun x acc -> (f x)@acc) l []
      let zero = []                                                             
      let ( >>= ) l f  = bind l f                                              
    end;; 
    

    首先,一个基本的用例:

    ["A";"B"] >>= fun (x ->
    [["C"];["D"]] >>= fun y -> x::y);;
    

    它返回 2 列表的乘积:[["A";"C"];["A";"D"];["B";"C"];["B";"D"]]

    而完整的用例(列表列表的产物),我们使用 List.fold :

     List.fold_right (fun x acc -> product x acc)
       [["a";"b"];["c";"d";"e"];["f";"g"]]     [[]];;
    

    将产生:

    [["a"; "c"; "f"]; ["a"; "c"; "g"]; ["a"; "d"; "f"]; ["a"; "d"; "g"];
     ["a"; "e"; "f"]; ["a"; "e"; "g"]; ["b"; "c"; "f"]; ["b"; "c"; "g"];
     ["b"; "d"; "f"]; ["b"; "d"; "g"]; ["b"; "e"; "f"]; ["b"; "e"; "g"]]
    

    `

    【讨论】:

      【解决方案3】:

      我是 Ocaml 的新手,所以也许有不同的方法

      let rec flat_map f xs =
        match xs with
        | [] -> []
        | x :: xs -> List.append (f x) (flat_map f xs);;
      val flat_map : ('a -> 'b list) -> 'a list -> 'b list = <fun>
      
      let product lists =
        let rec loop acc lists =
          match lists with
          | [] -> [[]]
          | first :: [] -> first |> List.map (fun x -> x :: acc)
          | first :: rest -> first |> flat_map (fun x -> loop (x :: acc) rest)
        in
          loop [] lists;;
      val product : 'a list list -> 'a list list = <fun>
      
      product [["A"; "B"]; ["D"; "E"; "F"; "G"]]
      - : string list list =
      [["D"; "A"]; ["E"; "A"]; ["F"; "A"]; ["G"; "A"]; ["D"; "B"]; ["E"; "B"];
       ["F"; "B"]; ["G"; "B"]]
      

      当然,它适用于任意数量的输入列表

      product [["1"; "2"; "3"]; ["A"; "B"; "C"; "D"]; ["+"; "-"]];;
      - : string list list =
      [["+"; "A"; "1"]; ["-"; "A"; "1"]; ["+"; "B"; "1"]; ["-"; "B"; "1"];
       ["+"; "C"; "1"]; ["-"; "C"; "1"]; ["+"; "D"; "1"]; ["-"; "D"; "1"];
       ["+"; "A"; "2"]; ["-"; "A"; "2"]; ["+"; "B"; "2"]; ["-"; "B"; "2"];
       ["+"; "C"; "2"]; ["-"; "C"; "2"]; ["+"; "D"; "2"]; ["-"; "D"; "2"];
       ["+"; "A"; "3"]; ["-"; "A"; "3"]; ["+"; "B"; "3"]; ["-"; "B"; "3"];
       ["+"; "C"; "3"]; ["-"; "C"; "3"]; ["+"; "D"; "3"]; ["-"; "D"; "3"]]
      

      也许他们用function读得更好

      let rec flat_map f = function
        | [] -> []
        | x :: xs -> List.append (f x) (flat_map f xs)
      
      let product lists =
        let rec loop acc = function
          | [] -> [[]]
          | first :: [] -> first |> List.map (fun x -> x :: acc)
          | first :: rest -> first |> flat_map (fun x -> loop (x :: acc) rest)
        in
          loop [] lists
      

      我们也可以从另一个角度来解决这个问题。注意输出顺序的不同

      let product lists =
        let rec loop acc = function
          | [] -> acc
          | first :: rest -> loop acc rest |> flat_map (fun c -> List.map (fun x -> x :: c) first)
        in
          loop [[]] lists;;
      val product : 'a list list -> 'a list list = <fun>
      
      product [["1"; "2"; "3"]; ["A"; "B"; "C"; "D"]; ["+"; "-"]];;
      - : string list list =
      [["1"; "A"; "+"]; ["2"; "A"; "+"]; ["3"; "A"; "+"]; ["1"; "B"; "+"];
       ["2"; "B"; "+"]; ["3"; "B"; "+"]; ["1"; "C"; "+"]; ["2"; "C"; "+"];
       ["3"; "C"; "+"]; ["1"; "D"; "+"]; ["2"; "D"; "+"]; ["3"; "D"; "+"];
       ["1"; "A"; "-"]; ["2"; "A"; "-"]; ["3"; "A"; "-"]; ["1"; "B"; "-"];
       ["2"; "B"; "-"]; ["3"; "B"; "-"]; ["1"; "C"; "-"]; ["2"; "C"; "-"];
       ["3"; "C"; "-"]; ["1"; "D"; "-"]; ["2"; "D"; "-"]; ["3"; "D"; "-"]]
      

      flat_map 以上为列表中的每个元素调用昂贵的 List.append。下面的变体收集中间结果,然后通过一次调用 List.concat 构建输出

      let flat_map f xs =
        let rec loop k = function
          | [] -> k []
          | x :: xs -> xs |> loop (fun r -> k (f x :: r))
        in
          loop List.concat xs;;
      val flat_map : ('a -> 'b list) -> 'a list -> 'b list = <fun>
      

      【讨论】:

      • 如果另一个 Ocaml 大师正在阅读这篇文章,我想知道如何将 product 转换为输出元组列表而不是列表列表的函数。
      • 由于 products 期望列表列表作为参数,并且由于未知长度的元组无法在类型系统中表示,因此您不能。如果您将两个参数作为输入而不是列表,那么生成元组将非常容易。
      • @PatJ 是有道理的。感谢您的评论^^
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2012-02-18
      • 2021-07-17
      • 2020-05-21
      • 1970-01-01
      相关资源
      最近更新 更多