1

我从函数式编程 / 开始fp-ts。我正在尝试编写一个函数,如果满足一个元素的条件,该函数会保留一个元素。

例子:

const condition = (i: number) => i % 10 === 0;
filterNext(condition, [19, 20, 3, 18, 8, 48, 20, 4, 10]) // => [3, 4]

我可能还需要扩展它,以便同时包含匹配和下一个元素,如:

const condition = (i: number) => i % 10 === 0;
filterNext(condition, [19, 20, 3, 18, 8, 48, 90, 4, 10]) // => [10, 3, 90, 4, 10]

我不知道如何为两者中的任何一个构建一个适当的纯函数。

欢迎任何提示。

4

2 回答 2

1

首先,让我向您展示代码:

import * as O from "fp-ts/lib/Option";
import * as A from "fp-ts/lib/Array";
import { pipe } from "fp-ts/lib/pipeable";

const filterOnPrevious = <T>(condition: (T) => boolean, xs: T[]): T[] =>
  pipe(
    A.tail(xs),
    O.getOrElse<T[]>(() => []),
    tail => A.zip(tail, xs),
    A.filter(([_, x]) => condition(x)),
    A.map(([x]) => x)
  );

其次,您编写“如果满足下一个元素的条件”,但您的示例表明该条件应该传递给一个元素。该代码适用于您的示例。

该任务分为三个部分:

  1. 为过滤器准备数据
  2. 筛选
  3. 提取你需要的东西

神奇的功能是Array.zip,它从两个数组创建对。

我使用原始数组和移位(删除第一个元素)数组运行它。这将创建对,其中对中的第一个值是原始值,第二个是应该运行条件的值。

因为Array.tail返回一个选项(如果数组为空则返回无),我们需要先运行它Option.getOrElse

在最后一步中,在过滤了这些对之后,我映射它们并从每对中获取原始值。

虽然我使用一对,但您可以使用对象或其他结构。我喜欢对,因为由于数组解构,它们易于键入和使用。

如何改变行为:

  • filterOnNext:切换A.zip(tail, xs)A.zip(xs, tail). 同样,第一个值是原始值,第二个是应该运行条件的值。或者,更新A.filterandA.map以便它使用该对中的另一个值。
  • 保持两者:这很棘手,因为如果例如连续三个数字满足条件,则不清楚期望的结果是什么。无论如何,如果条件传递第二个值,我仍然建议首先创建一个值作为结果的对。
于 2020-05-05T09:26:14.047 回答
0

让我知道这个是否奏效

import {Predicate, Refinement} from 'fp-ts/lib/function'
import {filterWithIndex} from 'fp-ts/lib/Array'
function filterWithNext<A>(predicate: Predicate<A>, xs: Array<A>): Array<A>
function filterWithNext<A, B extends A>(
  predicate: Refinement<A, B>,
  xs: Array<A>
): Array<B> {
  return filterWithIndex<A, B>(
    (i, x): x is B => predicate(x) && i !== xs.length && predicate(xs[i + 1])
  )(xs)
}
于 2020-05-05T05:31:51.520 回答