Ocaml 如何填充现有列表/数组

Ocaml 如何填充现有列表/数组,ocaml,reason,Ocaml,Reason,我对reason/ocaml/functional编程是新手 我知道List.append和[]@[]但这些函数将创建新列表,但如何填充现有列表/数组 填充列表的最佳方式是什么 填充阵列的最佳方法是什么?表示如果coords类型为let coords:array point=[] 或者对于这种情况,这是错误的流程(算法) 原因代码: type point = {x: int, y: int}; let coords: list point = []; let append raw =>

我对reason/ocaml/functional编程是新手

我知道
List.append
[]@[]
但这些函数将创建新列表,但如何填充现有列表/数组

  • 填充列表的最佳方式是什么
  • 填充阵列的最佳方法是什么?表示如果coords类型为
    let coords:array point=[]
  • 或者对于这种情况,这是错误的流程(算法)
  • 原因代码:

    type point = {x: int, y: int};
    
    let coords: list point = [];
    
    let append raw =>
      Array.iter
        (
          fun data => {
            let p = {x: data.x, y: data.y};
            /* how to append p to coords */
            ()
          }
        )
        raw;
    
    JS模拟:

    const coords = [];
    const append = raw => raw.forEach({x, y} => {
      coords.push({
        x: process(x),
        y: process(y)
      });
    });
    
    欢迎来到理性

    在Reason/OCaml中,列表是不可变的。在引擎盖下,它们是简单的单链表。每次“修改”它们时都会创建新的。下面是一个例子:

    let a = [1, 2, 3];
    let b = [0, ...a];
    
    这类似于JavaScript的数组“spread”,除了这里使用现有的
    a
    ,在前面链接一个新节点
    0
    ,并将其命名为b<代码>a仍然指向
    [1,2,3]
    (因此是“不可变的”)<代码>b现在是
    [0,1,2,3]
    。这是有效的,因为
    [1,2,3]
    部分是共享的

    这样做的好处是,您不必担心将列表传递给其他人,并且不小心让一个模糊的函数修改它。List的不变性允许您纯粹通过查看您当前所关注的值来推断代码(因为它永远不会改变!)

    列表的缺点是在末尾添加内容效率低下:

    let c = a @ [4] 
    
    该操作基本上是获取一个项目的列表,
    [4]
    ,然后依次将
    [1,2,3]
    的每个项目附加到该列表中。在性能方面是线性的。但从列表实现的简单性来看,历史上认为这是值得权衡的

    所以3。如果您试图设置列表项,那么这是错误的流程

  • 在您的情况下,填充列表的最佳方法是从旧列表中无变异地映射到列表:
    let newList=list.map(fun blablabla=>…)raw
  • 数组也一样。在上面画地图。有
    Array.of\u list
    Array.to\u list
    如果你被卡住了
  • 关于数组的更多信息:OCaml数组是可变的,其大小是不变的。把它想象成一块记忆。您可以通过
    array.make newSize
    分配一个新数组,然后通过
    array.set
    填充它。如果要大量调整数组的大小,那么这是没有意义的,因此请选择正确的数据结构

    对于JS编译,BuckleScript将ocaml数组编译为JS数组。因此,它是可变的和可调整大小的。您可以在下面找到熟悉的JS数组操作

    作为一种通用的启发式方法,如果您想更改长度:请尝试
    filter
    。如果要更改长度和包含的项目,请尝试
    向左折叠
    。否则,
    map

    最近,我们开始实现一些不可变、可调整大小、可选可变的数组。请继续收看

    欢迎来到理性

    在Reason/OCaml中,列表是不可变的。在引擎盖下,它们是简单的单链表。每次“修改”它们时都会创建新的。下面是一个例子:

    let a = [1, 2, 3];
    let b = [0, ...a];
    
    这类似于JavaScript的数组“spread”,除了这里使用现有的
    a
    ,在前面链接一个新节点
    0
    ,并将其命名为b<代码>a仍然指向
    [1,2,3]
    (因此是“不可变的”)<代码>b现在是
    [0,1,2,3]
    。这是有效的,因为
    [1,2,3]
    部分是共享的

    这样做的好处是,您不必担心将列表传递给其他人,并且不小心让一个模糊的函数修改它。List的不变性允许您纯粹通过查看您当前所关注的值来推断代码(因为它永远不会改变!)

    列表的缺点是在末尾添加内容效率低下:

    let c = a @ [4] 
    
    该操作基本上是获取一个项目的列表,
    [4]
    ,然后依次将
    [1,2,3]
    的每个项目附加到该列表中。在性能方面是线性的。但从列表实现的简单性来看,历史上认为这是值得权衡的

    所以3。如果您试图设置列表项,那么这是错误的流程

  • 在您的情况下,填充列表的最佳方法是从旧列表中无变异地映射到列表:
    let newList=list.map(fun blablabla=>…)raw
  • 数组也一样。在上面画地图。有
    Array.of\u list
    Array.to\u list
    如果你被卡住了
  • 关于数组的更多信息:OCaml数组是可变的,其大小是不变的。把它想象成一块记忆。您可以通过
    array.make newSize
    分配一个新数组,然后通过
    array.set
    填充它。如果要大量调整数组的大小,那么这是没有意义的,因此请选择正确的数据结构

    对于JS编译,BuckleScript将ocaml数组编译为JS数组。因此,它是可变的和可调整大小的。您可以在下面找到熟悉的JS数组操作

    作为一种通用的启发式方法,如果您想更改长度:请尝试
    filter
    。如果要更改长度和包含的项目,请尝试
    向左折叠
    。否则,
    map


    最近,我们开始实现一些不可变、可调整大小、可选可变的数组。请继续收看

    这里有几种不同的方法。如果您想坚持使用注释中提到的
    Js.Array
    ,可以这样做:

    使用及 如果你想用
    Array.fold_left
    Array.append
    来做,你可以按照@chenglou的建议用

            rawArray|>Array.fold_left((a, b) =>
            Array.append(a,  [|{x: b[0], y: b[1]}|]), [||]);
    
    它可能更干净,有一些辅助功能,如:

        let concatMap = f =>
          Array.fold_left((a, b) => Array.append(a, f(b)), [||]);
    
        let newPoint = coord => [|{x: coord[0], y: coord[1]}|];
    
    然后打电话:

        let rawTuples = [|(3, 5), (4, 9), (9, 4)|];
    
        let arrayTuples = rawTuples |> concatMap(a => [|{x: fst(a), y: snd(a)}|]);
    
    使用助手也有助于我理解函数的每个部分在做什么

    也可以使用
    元组
    ,因为它们只是ReasonML/Ocaml/Rescript中的数组

        let rawTuples = [|(3, 5), (4, 9), (9, 4)|];
    
        let arrayTuples = rawTuples |> concatMap(a => [|{x: fst(a), y: snd(a)}|]);
    
    使用 您可以选择使用
    可变记录创建数组

    这就是更新可变记录数组的方法。
        let rawTuples = [|(3, 5), (4, 9), (9, 4)|];
    
        let arrayTuples = rawTuples |> concatMap(a => [|{x: fst(a), y: snd(a)}|]);
    
        let printCoords = coords => Array.iter(Js.log, coords);
    
        type mutablePoint('a, 'b) = {
          mutable x: 'a,
          mutable y: 'b,
        };
    
        let data1: mutablePoint(int, int) = {x: 2, y: 4};
    
        let data2: mutablePoint(int, int) = {x: 3, y: 4};
    
        let data3: mutablePoint(int, int) = {x: 4, y: 4};
    
        let isEven = n => {
          n mod 2 == 0;
        };
    
        let multiplyByY = data => data.x = data.x * data.y;
    
        let makeItOdd = data => data.x = data.x + 1;
    
        let updateData = data => data.x->isEven
           ? data->makeItOdd :  multiplyByY(data);
    
        let points: array(mutablePoint('x, 'y)) = [|data1, data2, data3|];
    
        let append = (fn, data) => {
          Array.iter(x => {fn(x)}, data);
    
          data;
        };
    
        points |> append(updateData);
    
        Js.log("points after");
    
        printCoords(points);
    
        // points after { x: 3, y: 4 } { x: 12, y: 4 }{ x: 5, y: 4 }
    
        let points2: array(mutablePoint('x, 'y)) = [|data1, data2, data3|];
    
        let printCoords = coords => Array.iter(Js.log, coords);
    
        printCoords(points2);
    
        let rawData = [|[|1, 2|], [|3, 4|], [|9, 4|]|];
    
        let update_x_on_point_i = (i, x) => points2[i].x = x;
    
        let append = raw =>
          Array.iteri(
            (i, d) => {
              let x: int = d[0];
    
              update_x_on_point_i(i, x);
    
            },
            raw,
          );
    
        append(rawData);
    
        Js.log2("points2 after: ", points2);
    
        printCoords(points2);
    
        // points2 after:  [ { x: 1, y: 4 }, { x: 3, y: 4 }, { x: 9, y: 4 } ]
    
        type point = {
          x: int,
          y: int,
        };
        let coords = [|{x: 9, y: 7}, {x: 2, y: 4}, {x: 3, y: 8}|];
    
        Js.log("coords before");
    
        Js.log("-------");
    
        let append = raw =>
          raw->Belt.Array.mapWithIndex(
                 _,
                 (i, r) => {
                   let new_point_i = {x: r[0], y: r[1]};
                   coords[i] = new_point_i;
                 },
               );
    
        let rawData = [|[|1, 2|], [|3, 4|], [|9, 4|]|];
    
        append(rawData);
    
        Js.log("coords after");
    
        Js.log(coords);
        coords before
        [ { x: 9, y: 7 }, { x: 2, y: 4 }, { x: 3, y: 8 } ]
         -------
        coords after
        [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 9, y: 4 } ]
    
    type data_item = {
      symbol: string,
      next: bool,
    };
    
    /* Convenience function for making data items. */
    let make_data_item = (symbol, next) => {symbol, next};
    
    let process = (data: list(data_item)) => {
      /*
       We track the current symbol as well as the result list in the folding function.
       */
      let fold_func = ((current, result), {symbol, next}) =>
        if (next) {
          (symbol, [current, ...result]);
        } else {
          (current ++ symbol, result);
        };
    
      let (current, result) = List.fold_left(fold_func, ("", []), data);
      /*
       We need to reverse the result list because `[el, ...els]` above actually
       builds it up in the _opposite_ order to the original input list.
       */
      (current, List.rev(result));
    };
    
    let result =
      process([
        make_data_item("a", false),
        make_data_item("b", false),
        make_data_item("c", true),
        make_data_item("d", false),
      ]);
    /* result = ("cd", ["ab"]) */