【力扣】2629. 复合函数——函数组合

news2025/3/13 0:10:34

【力扣】2629. 复合函数——函数组合

文章目录

    • 【力扣】2629. 复合函数——函数组合
    • 题目
    • 解决方案
      • 概述
      • 方法 1:使用迭代的函数组合
        • 概述
        • 算法
        • 实现
        • 复杂度分析
      • 方法 2:使用 Array.reduceRight() 的函数组合
        • 概述
        • 算法
        • 实现
        • 复杂度分析
      • 附加考虑
        • 处理 this 上下文
        • 使用外部库

题目

请你编写一个函数,它接收一个函数数组 [f1, f2, f3,…, fn] ,并返回一个新的函数 fn ,它是函数数组的 复合函数

[f(x), g(x), h(x)]复合函数fn(x) = f(g(h(x)))

一个空函数列表的 复合函数恒等函数 f(x) = x

你可以假设数组中的每个函数接受一个整型参数作为输入,并返回一个整型作为输出。

示例 1:

输入:functions = [x => x + 1, x => x * x, x => 2 * x], x = 4
输出:65
解释:
从右向左计算......
Starting with x = 4.
2 * (4) = 8
(8) * (8) = 64
(64) + 1 = 65

示例 2:

输入:functions = [x => 10 * x, x => 10 * x, x => 10 * x], x = 1
输出:1000
解释:
从右向左计算......
10 * (1) = 10
10 * (10) = 100
10 * (100) = 1000

示例 3:

输入:functions = [], x = 42
输出:42
解释:
空函数列表的复合函数就是恒等函数

提示:

  • -1000 <= x <= 1000
  • 0 <= functions.length <= 1000
  • 所有函数都接受并返回一个整型

解决方案

概述

函数组合是函数式编程中的一个概念,即一个函数的输出被用作另一个函数的输入。换言之,它是将两个或更多函数链接在一起,以使一个函数的结果成为下一个函数的输入的过程。

例如,这里有两个函数,f(x) 和 g(x):

const f = x => x + 2;
const g = x => x * 3;

这两个函数的组合,表示为 (f ∘ g)(x),即首先应用函数 g(x),然后使用g(x)的结果作为f(x)的输入。在这种情况下,(f ∘ g)(x) 就是:

const composedFunc = x => f(g(x)); // f(g(x)) = f(3x) = 3x + 2

因此,当我们组合函数f(x)g(x)时,得到的函数(f ∘ g)(x)接受一个输入 x,将其乘以 3(使用 g(x)),然后再加上 2 (使用 f(x))。

const composedFunc = x => f(g(x)); // f(g(x)) = f(3x) = 3x + 2

在这个问题中,你需要根据给定的函数数组,创建一个表示给定函数数组组合的单一函数。这里的挑战在于你需要了解如何将函数链接在一起并将一个函数的输出传递给下一个函数的输入。

在函数数组为空的情况下,组合函数应该充当标识函数,即 f(x) = x。换言之,函数应返回传递给它的任何内容且不进行任何修改。

∘符号(f ∘ g)(x)在数学中用于表示函数组合。它读作 “f g 的组合" 或 “g f”。fg之间的小圆圈 (∘) 是组合运算符。此符号用来表示首先将函数g(x)应用,然后使用结果作为函数f(x)的输入值。换句话说,首先应用 g(x),然后将结果用作f(x)的输入。

方法 1:使用迭代的函数组合

概述

函数组合是一个概念,其中我们按指定的顺序将一系列函数应用于输入值。在这个问题中,我们需要组合数组中给定的函数,并创建一个表示它们组合的新函数。应用这些函数的顺序是从右到左的,当函数数组为空时,我们应该返回标识函数,它返回的输入值保持不变。

为了解决这个问题,我们可以从数组的最后一个函数开始反向迭代,并依次将每个函数应用于输入值。我们将从输入值 x 开始,应用数组中的最后一个函数。然后,我们将使用结果作为前一个函数的输入,并继续这个过程,直到我们到达数组中的第一个函数。在应用所有函数后,我们将返回最终的结果。

算法
  1. compose函数内部,返回另一个接受输入值x的函数。
  2. 检查函数数组的长度是否为零;如果是,则返回标识函数(即返回 x)。
  3. 使用值x初始化变量 input
  4. 从最后一个索引到第一个索引遍历函数数组。
  5. 对数组中的每个函数,将其应用于input值并使用结果更新input值。
  6. 在遍历所有函数之后,将最终输入值作为组合函数的输出返回。
实现

以下是使用常规for循环的实现:

var compose = function (functions) {
  return function (x) {
    if (functions.length === 0) return x;
    let input = x;

    for (let i = functions.length - 1; i >= 0; i--) {
      const currFunc = functions[i];

      input = currFunc(input);
    }

    return input;
  };
};

使用for ... of循环的实现:

var compose = function (functions) {
  return function (x) {
    if (functions.length === 0) return x;
    let input = x;

    for (const func of functions.reverse()) {
      input = func(input);
    }

    return input;
  };
};
复杂度分析

设 N 为数组中的函数数量。
时间复杂度:O(N)。假设每个函数的时间复杂度是常数,那么数组中的每个函数都将被调用一次。
空间复杂度:O(1)。迭代方法使用单个变量input来存储中间结果,不需要额外的空间。

方法 2:使用 Array.reduceRight() 的函数组合

概述

在第一种方法中,我们使用迭代的方式从右到左应用函数。不过,我们可以利用Array.reduceRight() 方法来实现相同的结果。reduceRight() 方法将一个函数应用于累加器和数组中的每个元素(从右到左),以将它们还原为单个值。在这种情况下,我们的累加器将是输入值 x,函数将是数组中函数的组合。

使用reduceRight()简化了代码,并提供了更函数式编程风格的解决方案。关键是理解Array.reduceRight()方法的工作原理以及如何将其应用于此问题。

算法
  1. compose函数内部,返回另一个接受输入值 x 的函数。
  2. 使用Array.reduceRight()方法从右到左迭代函数。
  3. 对数组中的每个函数,将其应用于累加器(初始为 x)并使用结果更新累加器。
  4. 在迭代所有函数后,将最终累加器值作为组合函数的输出返回。
实现
var compose = function(functions) {
    return x => functions.reduceRight((acc, f) => f(acc), x);
};

关键在于理解Array.reduceRight()方法的工作原理。
Array.reduceRight() 是内置的 JavaScript 数组方法,用于对数组的每个元素应用一个函数,从右到左进行迭代。它需要两个参数:一个reducer函数和一个可选的累加器的初始值。
reducer 函数本身有四个参数:累加器、当前值、当前索引和正在处理的数组。累加器是一个在每次迭代中建立的值,最终在过程结束时返回。在我们的情况下,累加器表示应用组合函数中的函数时的中间结果。
以下是在 compose 函数上下文中Array.reduceRight()的工作方式的详细说明:

  1. compose 函数接收一个包含函数的数组,并返回一个新的函数,该函数接受输入值 x。
  2. 当使用输入值 x 调用组合函数时,它在函数数组上调用 Array.reduceRight()
  3. reducer 函数对数组中的每个函数进行迭代,从最右边的元素开始向左移动。累加器最初包含输入值 x。
  4. 在每次迭代中,reducer 函数将当前函数应用于累加器,并使用结果更新累加器。
  5. 一旦所有函数都已应用,将返回累加器的最终值。

为了说明这个过程,让我们考虑一个简单的例子:

const functions = [x => x * 2, x => x + 1];
const composedFn = compose(functions);
const result = composedFn(3); // 结果应该是 (3 + 1) * 2 = 8
  1. compose 函数接收一个包含两个函数的数组:x => x * 2 x => x + 1
  2. 当使用输入值3调用 composedFn 时,它在函数数组上调用 Array.reduceRight()
  3. reducer 函数从最右边的函数x => x + 1开始,将它应用于累加器(最初为 3)。累加器变为 3 + 1 = 4
  4. reducer 函数然后移到下一个函数 x => x * 2,并将其应用于累加器(现在为 4)。累加器变为 4 * 2 = 8
  5. 累加器的最终值8被返回作为组合函数的结果。

总之,通过使用 Array.reduceRight(),我们可以用轻松简洁的方式应用函数组合。

复杂度分析

设 N 为数组中的函数数量。
时间复杂度:O(N)。假设每个函数的时间复杂度是常数,那么数组中的每个函数都将被调用一次。
空间复杂度:O(1)reduceRight 方法使用累加器来存储中间结果,不需要额外的空间。

附加考虑

在处理函数组合时,一个专业的实现需要考虑更多的事情。

处理 this 上下文

在 JavaScript 中,函数内的this值取决于如何调用函数。在使用函数组合时,重要的是考虑如何保留原始函数的this上下文。尽管提供的测试用例可能不会明确测试这一点,但在实际场景中,正确处理原始函数的this上下文可能非常关键。
处理 this 上下文的一种方法是在调用组合函数时使用 call 或apply方法。这允许你在调用函数时显式设置this的值。例如:

const composedFn = function(x) {
  let result = x;
  for (let i = functions.length - 1; i >= 0; i--) {
    result = functions[i].call(this, result);
  }
  return result;
};

这可以确保原始函数的this上下文在作为组合函数的一部分被调用时得以保留。为了更彻底的理解,请你考虑这样一种情况:你合成的函数是对象上的方法,它们依赖于this来访问该对象上的其他属性或方法。如果在组合这些方法时没有正确保存this上下文,它们可能无法正常运行。

const obj = {
  value: 1,
  increment: function() { this.value++; return this.value; },
  double: function() { this.value *= 2; return this.value; },
};

// 组合方法而不保留 `this`
const badCompose = function(functions) {
  return function(x) {
    let result = x;
    for (let i = functions.length - 1; i >= 0; i--) {
      result = functions[i](result);
    }
    return result;
  };
};

const badComposedFn = badCompose([obj.increment, obj.double]);
console.log(badComposedFn(1));  // 这将返回 NaN,因为 `this` 不是 `obj` 内部的 `increment` 和 `double`

为了处理这种情况,你可以在调用组合函数时使用 call apply方法,这样就可以显式设置函数调用时的this值。

const obj = {
  value: 1,
  increment: function() { this.value++; return this.value; },
  double: function() { this.value *= 2; return this.value; },
};

// 在保留 `this` 的同时编写方法
const goodCompose = function(functions, context) {
  return function(x) {
    let result = x;
    for (let i = functions.length - 1; i >= 0; i--) {
      result = functions[i].call(context, result);
    }
    return result;
  };
};

const goodComposedFn = goodCompose([obj.increment, obj.double], obj);
console.log(goodComposedFn(1));  // 这是预期之中的,因为`this`是`increment`和`double`中的`obj`
使用外部库

你可以考虑使用提供组成实现功能的外部库而不是自己编写。诸如RamdaLodash之类的库提供各种实用函数,包括函数组合。通过使用知名库,你可以获得以下好处:

  1. 稳健性:这些库通过了广泛的测试,并被许多开发人员使用,可以确保其实现是可靠的并处理各种边缘情况。
  2. 性能:这些库经过性能优化,可能具有比自定义实现更好的性能特性。
  3. 可读性:使用流行库可以提高代码的可读性,因为其他开发人员更有可能熟悉常用外部库的函数及其行为。
  4. 文档:知名库通常具有全面的文档。这可以极大地简化开发过程,因为你可以快速查阅文档以获取函数解释、使用示例等信息。此外,许多现代代码编辑器都支持例如在函数上悬停以显示简要描述和指向更详细文档链接的功能。这对于方便的了解库函数的预期行为非常有帮助,而且无需不断切换到 Web 浏览器。

以下是使用Ramdacompose函数的示例:

import { compose } from 'ramda';

const composedFn = compose(...functions);

以及使用LodashflowRight函数的示例:

import { flowRight } from 'lodash';

const composedFn = flowRight(...functions);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2313998.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络协议安全】任务10:三层交换机配置

CSDN 原创主页&#xff1a;不羁https://blog.csdn.net/2303_76492156?typeblog三层交换机是指在OSI&#xff08;开放系统互连&#xff09;模型中的第三层网络层提供路由功能的交换机。它不仅具备二层交换机的交换功能&#xff0c;还能实现路由功能&#xff0c;提供更为灵活的网…

依托大数据实验室建设,培育创新人才:数据科学与大数据技术专业人才培养实践

近年来&#xff0c;得益于全球大数据产业政策扶持与数字经济蓬勃发展&#xff0c;大数据市场呈现迅猛增长态势。国家层面相继出台《“数据要素”三年行动计划&#xff08;2024—2026年&#xff09;》《数字中国建设整体布局规划》等政策&#xff0c;旨在激发产业创新活力&#…

如何使用 CSS 实现黑色遮罩效果

最近在工作中遇见了一个需求&#xff0c;鼠标经过盒子出现黑色遮罩&#xff0c;遮罩中有相关的编辑按钮&#xff0c;点击以后&#xff0c;进行图片上传并且展示&#xff0c;由于当时没有思路&#xff0c;思考了好久&#xff0c;所以在完成开发后进行总结&#xff0c;使用的技术…

ChatGPT课件分享(37页PPT)

资料解读&#xff1a;ChatGPT课件分享 详细资料请看本解读文章的最后内容。 近年来&#xff0c;人工智能技术的迅猛发展引发了全球范围内的广泛关注&#xff0c;尤其是以OpenAI为代表的公司在自然语言处理领域的突破性进展&#xff0c;彻底改变了人机交互的方式。本文将详细解…

无人机扩频技术对比!

一、技术原理与核心差异 FHSS&#xff08;跳频扩频&#xff09; 核心原理&#xff1a;通过伪随机序列控制载波频率在多个频点上快速跳变&#xff0c;收发双方需同步跳频序列。信号在某一时刻仅占用窄带频谱&#xff0c;但整体覆盖宽频带。 技术特点&#xff1a; 抗干扰…

C语言_数据结构总结4:不带头结点的单链表

纯C语言代码&#xff0c;不涉及C 0. 结点结构 typedef int ElemType; typedef struct LNode { ElemType data; //数据域 struct LNode* next; //指针域 }LNode, * LinkList; 1. 初始化 不带头结点的初始化&#xff0c;即只需将头指针初始化为NULL即可 void Init…

几种常见的虚拟环境工具(Virtualenv、Conda、System Interpreter、Pipenv、Poetry)的区别和特点总结

在 PyCharm 中创建虚拟环境是一个非常直接的过程&#xff0c;可以帮助你管理项目依赖&#xff0c;确保不同项目之间的依赖不会冲突。 通过 PyCharm 创建虚拟环境 打开 PyCharm 并选择或创建一个项目。 打开项目设置&#xff1a; 在 Windows/Linux 上&#xff0c;可以通过点击…

Ubuntu安装问题汇总

参考文章&#xff1a; 【Ubuntu常用快捷键总结】 【王道Python常用软件安装指引】 1. 无法连接虚拟设备 sat0:0 【问题】&#xff1a;出现下图所示弹框。 【问题解决】&#xff1a; 点击 “否” 。 点击左上角的 “虚拟机” → “设置…” → “CD/DVD (SATA)” &#xff0c;…

Ceph(1):分布式存储技术简介

1 分布式存储技术简介 1.1 分布式存储系统的特性 &#xff08;1&#xff09;可扩展 分布式存储系统可以扩展到几百台甚至几千台的集群规模&#xff0c;而且随着集群规模的增长&#xff0c;系统整体性能表现为线性增长。分布式存储的水平扩展有以下几个特性&#xff1a; 节点…

从0开始的操作系统手搓教程43——实现一个简单的shell

目录 添加 read 系统调用&#xff0c;获取键盘输入 :sys_read putchar和clear 上班&#xff1a;实现一个简单的shell 测试上电 我们下面来实现一个简单的shell 添加 read 系统调用&#xff0c;获取键盘输入 :sys_read /* Read count bytes from the file pointed to by fi…

【Spring】基础/体系结构/核心模块

概述&#xff1a; Spring 是另一个主流的 Java Web 开发框架&#xff0c;该框架是一个轻量级的应用框架。 Spring 是分层的 Java SE/EE full-stack 轻量级开源框架&#xff0c;以 IoC&#xff08;Inverse of Control&#xff0c;控制反转&#xff09;和 AOP&#xff08;Aspect…

01 音视频知识学习(视频)

图像基础概念 ◼像素&#xff1a;像素是一个图片的基本单位&#xff0c;pix是英语单词picture的简写&#xff0c;加上英 语单词“元素element”&#xff0c;就得到了“pixel”&#xff0c;简称px&#xff0c;所以“像素”有“图像元素” 之意。 ◼ 分辨率&#xff1a;是指图像…

vue3自定义hooks遇到的问题

问题 写了一个输入查询参数和url返回加载中状态、请求方法、接口返回列表的hooks&#xff0c;出现的结果是只有请求方法有效&#xff0c;加载状态无效&#xff0c;接口返回了数据&#xff0c;页面却不显示数据。 代码如下 只展示部分关键代码 import { ref, toRefs, toRef, o…

liunx磁盘挂载和jar启动命令

一、磁盘挂载 查看历史磁盘挂载命令&#xff1a;history | grep mount 查看所有挂载硬盘命令&#xff1a;mount 磁盘挂载命令&#xff1a;mount -t cifs -o usernamesh**,passwordP!ss**** //192.168.1.2/attachmentfilesShare2.2/pdfCert /home/nybzg/cnfai1/pdfCert 二、j…

gbase8s rss集群通信流程

什么是rss RSS是一种将数据从主服务器复制到备服务器的方法 实例级别的复制 (所有启用日志记录功能的数据库) 基于逻辑日志的复制技术&#xff0c;需要传输大量的逻辑日志,数据库需启用日志模式 通过网络持续将数据复制到备节点 如果主服务器发生故障&#xff0c;那么备用服务…

如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统

我在业余时间开发了一款自己的独立产品&#xff1a;升讯威在线客服与营销系统。陆陆续续开发了几年&#xff0c;从一开始的偶有用户尝试&#xff0c;到如今线上环境和私有化部署均有了越来越多的稳定用户。 随时近来 AI 大模型的火热&#xff0c;越来越多的客户&#xff0c;问…

【AI智能体报告】开源AI助手的革命:OpenManus深度使用报告

一、引言&#xff1a;当开源智能体走进生活 2025年3月&#xff0c;MetaGPT团队用一场"开源闪电战"改写了AI Agent的竞争格局。面对商业产品Manus高达10万元的邀请码炒作&#xff0c;他们仅用3小时便推出开源替代品OpenManus&#xff0c;首日即登顶GitHub趋势榜。 …

DeepSeek+Maxkb+Ollama+Docker搭建一个AI问答系统

DeepSeekMaxkbOllamaDocker搭建一个AI问答系统 文章目录 DeepSeekMaxkbOllamaDocker搭建一个AI问答系统前言一、创建同一内网的网络二、拉取两个镜像三、启动Ollama以及调试Maxkb4.Maxkb创建一个应用并建立知识库5、应用效果总结 前言 我觉得只要是使用Docker技术&#xff0c;…

江科大51单片机笔记【12】DS18B20温度传感器(上)

写在前言 此为博主自学江科大51单片机&#xff08;B站&#xff09;的笔记&#xff0c;方便后续重温知识 在后面的章节中&#xff0c;为了防止篇幅过长和易于查找&#xff0c;我把一个小节分成两部分来发&#xff0c;上章节主要是关于本节课的硬件介绍、电路图、原理图等理论…

P8662 [蓝桥杯 2018 省 AB] 全球变暖--DFS

P8662 [蓝桥杯 2018 省 AB] 全球变暖--dfs 题目 解析讲下DFS代码 题目 解析 这道题的思路就是遍历所有岛屿&#xff0c;判断每一块陆地是否会沉没。对于这种图的遍历&#xff0c;我们首先应该想到DFS。 代码的注意思想就是&#xff0c;在主函数中遍历找出所有岛屿&#xff0c…