【问题标题】:How about a higher-order function called struggle?一个叫做斗争的高阶函数怎么样?
【发布时间】:2017-12-26 19:00:30
【问题描述】:

想象一下你的妻子让你去买一些 Campion 花。因为它是一种稀有的花,你不确定你是否能在隔壁的花店找到它。所以你必须检查镇上的每一家花店并要求他们露营。但事实上有些花店有公用电话号码,你可以打电话问他们。其中一些有网站,其中一些你必须走进去。

因此,您从最简单的解决方案开始您的探索。关键是您以独特的方式检查每家花店,例如您与他们交谈的方式或到达那个人的路径。如果我们从代码上讲,这些是几个功能;每个商店一个功能。

因此,在编程世界中,您必须为 10 种可能的解决方案编写 10 种不同的查询函数。下一步是逐个尝试列表中所有可能的解决方案,一旦您得到肯定的答案/回复/返回,您将停止任务并提出答案。

我认为这是一个例行程序,最有可能像过滤器、映射和缩减器,我相信我是对的。

那么有这样的东西怎么样?

在二郎中:

-export([struggle/2]).

struggle(Input, Solutions) ->
    struggle(Input, Solutions, false).

struggle(_, [], false) ->
    false;
struggle(Input, [Solution|OtherSolutions], false) ->
    struggle(Input, OtherSolutions, Solution(Input));
struggle(_, _, Answer) ->
    Answer.

或在 javascript 中:

function struggle(input, solutions)
{
    let strgl = (input, solutions, acc) => {
        if (solutions.length === 0 && acc === undefined) return undefined;
        if (acc === undefined) return strgl(input, solutions.slice(1), solutions[0](input));
        return acc;
    };
    return strgl(input, solutions, undefined);
}

你不认为它是一个内置的高阶函数就足够通用了吗?

PS:代码运行良好,无需修复。问题是,为什么不是内置函数?

【问题讨论】:

  • 有点像Promise.race ?
  • 我不知道 Erlang,但是你在找Array.prototype.find吗?
  • solutions.map(f => f(input)).find(ans => ans !== undefined);
  • @4castle, no Array.prototype.findArray.prototype.filter 仅在所有数据都在一个位置或至少共享一个数据结构(将它们连接为具有一个数据源)时才有效。我说的是一种情况,您需要不同的函数体,其中一个可能是ans => ans !== undefined,但不是全部。
  • @T4rk1n 不知何故但不完全是。因为Promise.race 同时执行所有可能的解决方案并返回第一个resolve 它从那些称为承诺的异步中获得。这里我们需要一个顺序/优先级。

标签: javascript functional-programming erlang


【解决方案1】:

具体的问题是什么?

strugle(Input, Solutions) ->
  lists:any(fun(X) -> X(Input) end, Solutions).

它是 Erlang 的必需品。或者,如果您希望返回第一个非 false 值。

strugle(Input, Solutions) ->
  any_non_false(fun(X) -> X(Input) end, Solutions).

any_non_false(F, L) ->
  try lists:any(fun(X) ->
      case F(X) of
        false -> false;
        Y -> throw({return, Y})
      end
    end, L)
  catch {return, X} -> X end.

【讨论】:

  • 这仅在函数返回truefalse 时有效。我相信 OP 希望能够从函数返回任何值并返回第一个非 false 值。至少这就是 OP 的 Erlang 和 JS 代码所做的(JS 代码返回第一个非 undefined 值)。
【解决方案2】:

在 Scheme 中,您可以这样编写代码:

// some from SRFI-1, also called ormap in some dialects
(define (struggle input solutions)
  (some (lambda (f) (f input)) solutions))

似乎 JavaScript 得到了 Array.prototype.some 而 Erlang 得到了 lists:any/2 但两者都有缺陷,因为它们不返回真实值而只返回 true 所以你只能知道它是否找到了答案而不是什么答案是。

在 JS 中,创建与 Scheme 相同的 some 版本很简单。我不确定 Erlang。

【讨论】:

    【解决方案3】:

    这听起来很像Array.prototype.find,只是你想要的返回值是find 看到的第一个真值,而不是产生真值的元素。

    您可以使用Array.prototype.reduce 来完成此操作:

    function struggle(input, solutions) {
      return solutions.reduce((ans, solution) =>
        ans !== undefined ? ans : solution(input)
      , undefined);
    }
    

    命令式地写成这样:

    function struggle(input, solutions) {
      for (const solution of solutions) {
        const ans = solution(input);
        if (ans !== undefined) {
          return ans;
        }
      }
      return undefined;
    }
    

    这听起来不像是一个内置函数,因为它不够通用。这里有两个步骤:(1)映射解决方案,以及(2)测试该值是否可以接受。内置的 JavaScript 函数往往是非常通用的。更具体的函数更适合实用程序库。

    【讨论】:

    • 你知道你的 reducer 版本需要多余的比较步骤,即使找到了答案。我知道进行简单的ans !== undefined 调用可能没有真正的性能影响,但无论如何它都是多余的,我知道问题是这种循环没有break,因为在非循环中根本没有break命令式代码。
    • 对。函数式语言通常通过惰性支持短路。 JavaScript 被急切地评估,因此您需要使用 JavaScript 的一些命令式特性、为您执行此操作的库或像您最初那样使用递归来编写它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    相关资源
    最近更新 更多