当前位置: 首页 > article >正文

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

【力扣】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);

相关文章:

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

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

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

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

Linux 服务器安全配置:密码复杂度与登录超时设置

Linux服务器安全配置指南:密码复杂度与登录超时设置 一、密码复杂度设置 通过PAM模块pam_cracklib.so实现密码强度策略,配置文件: system-auth该文件主要用于定义系统范围内的认证策略,涵盖了用户登录、su 命令切换用户、sudo 权限提升等多种认证场景。当用户尝试进行系…...

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

近年来&#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;彻底改变了人机交互的方式。本文将详细解…...

开源模型时代的 AI 开发革命:Dify 技术深度解析

开源模型时代的AI开发革命&#xff1a;Dify技术深度解析 引言&#xff1a;AI开发的开源新纪元 在生成式AI技术突飞猛进的2025年&#xff0c;开源模型正成为推动行业创新的核心力量。据统计&#xff0c;全球超过80%的AI开发者正在使用开源模型构建应用&#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…...

Zama TFHE-rs v1.0 发布

1. 引言 2025年2月&#xff0c;Zama 发布了 TFHE-rs v1.0&#xff0c;这是 TFHE-rs 库的第一个稳定版本。这标志着一个重要的里程碑&#xff0c;稳定了 x86 CPU 后端的高级 API&#xff0c;同时确保了向后兼容性。——即&#xff0c;现在可以依赖 TFHE-rs API&#xff0c;而不…...

AArch64架构及其编译器

—1.关于AArch64架构 AArch64是ARMv8-A架构的64位执行状态&#xff0c;支持高性能计算和大内存地址空间。它广泛应用于现代处理器&#xff0c;如苹果的A系列芯片、高通的Snapdragon系列&#xff0c;以及服务器和嵌入式设备。 • 编译器&#xff1a;可以使用GCC、Clang等编译器编…...

【ISP】对于ISP的关键算法补充

本篇是对于ISP的关键算法进行补充说明&#xff0c; 后面我们将开始逐渐深入讨论ISP的pipeline 1. 非局部均值&#xff08;NLM, Non-Local Means&#xff09; 原理 非局部均值&#xff08;NLM&#xff09;是一种基于 块匹配&#xff08;Patch Matching&#xff09; 的去噪算法…...

几种常见的虚拟环境工具(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…...

用Python和Docker-py打造高效容器化应用管理利器

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着容器化技术的发展,Docker已成为现代化应用部署的核心工具。然而,手动管理容器在规模化场景下效率低下。本文深入探讨如何利用Python结…...

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;那么备用服务…...

使用 OpenSSL 和 Python 实现 AES-256-CBC 加密与解密(安全密钥管理)

环境 OpenSSLPython 使用 OpenSSL 加密 1. 生成 AES 密钥和 IV 强烈推荐使用方法一&#xff08;Python secrets 模块&#xff09;&#xff0c;因为它更安全。 方法一: Python 的 secrets 模块&#xff08;安全方式&#xff09; 不要使用 OpenSSL 的 rand 命令直接生成密钥…...

1-001:MySQL的存储引擎有哪些?它们之间有什么区别?

MySQL 存储引擎 ├── InnoDB&#xff08;默认引擎&#xff09; │ ├── 事务支持&#xff1a;支持 ACID 和事务&#xff08;事务日志、回滚、崩溃恢复&#xff09; │ ├── 锁机制&#xff1a;支持行级锁&#xff0c;提高并发性能 │ ├── 外键支持&#xff1a;支持外键…...

持续集成与部署(CI/CD)实践指南:测试工程师的效率革命之路

一、引言 在当今快速发展的软件开发领域&#xff0c;效率和质量是至关重要的。随着软件项目的规模和复杂度不断增加&#xff0c;传统的开发和测试流程逐渐暴露出诸多问题&#xff0c;如开发周期长、集成困难、测试覆盖不足以及部署风险高等。持续集成&#xff08;Continuous I…...

C盘清理技巧分享:释放空间,提升电脑性能

目录 1. 引言 2. C盘空间不足的影响 3. C盘清理的必要性 4. C盘清理的具体技巧 4.1 删除临时文件 4.2 清理系统还原点 4.3 卸载不必要的程序 4.4 清理下载文件夹 4.5 移动大文件到其他盘 4.6 清理系统缓存 4.7 使用磁盘清理工具 4.8 清理Windows更新文件 4.9 禁用…...

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

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

能否调整爬虫以支持多页商品列表?

当然可以&#xff01;调整爬虫以支持多页商品列表是一个常见的需求&#xff0c;尤其是在商品数量较多时。通过分析目标网站的分页机制&#xff0c;可以实现自动翻页并获取多页商品列表。以下是如何调整爬虫代码以支持多页商品列表的详细步骤和代码示例。 一、分析分页机制 首…...

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

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

Python 逆向工程:2025 年能破解什么?

有没有想过在复杂的软件上扭转局面&#xff1f;到 2025 年&#xff0c;Python 逆向工程不仅仅是黑客的游戏&#xff0c;它是开发人员、安全专业人员和好奇心强的人解开编译代码背后秘密的强大方法。无论您是在剖析恶意软件、分析 Python 应用程序的工作原理&#xff0c;还是学习…...