ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

函数是什么?一次讲清数学、编程、Excel与AI中的函数

函数是什么?一次讲清数学、编程、Excel与AI中的函数 函数概念详解最近后台收到不少留言发现一个很有意思的现象大家在搜索函数这两个字时想问的东西五花八门。有人翻着高中数学课本问对应法则有人在命令行里看到无法将pnpm项识别为cmdlet、函数、脚本文件或可运行程序的名称这一串报错有人抱着Excel表格找VLOOKUP的用法还有人对着softmax损失函数一头雾水。这些场景看似毫无关联骨子里却都指向同一个概念——函数。这篇文章我想做一件事把散落在数学、编程、办公软件、机器学习里的函数串起来讲清楚它们共享的那条逻辑主线再逐个拆开来看每个领域里的特殊形态。不管你是因为哪条热搜词点进来的看完应该都能找到自己需要的答案顺便弄明白其他领域的人提到函数时到底在说什么。1. 函数的本质那个从输入到输出的确定性映射1.1 数学课本里的定义为什么拗口所有关于函数的困惑最终都要回到数学课上那句经典定义函数是两个非空集合之间的映射对于定义域中的每一个元素在值域中都有唯一确定的元素与之对应。很多人在这一步就被劝退了其实拆开来看很简单。定义域就是所有合法输入的集合值域是所有可能输出的集合函数的本质就是一条规则你给我一个输入我按照固定规则给你一个输出而且同一个输入永远对应同一个输出。举一个生活化的例子。你投硬币进自动售货机按了可乐的按钮出来的永远是可乐不会有时候是雪碧。你的投币和按键就是输入掉出来的饮料就是输出这台售货机内部的处理逻辑就是一个函数。如果它心情好给你可乐、心情不好给你白水那就不是一个合格的函数因为它没有确定性。这个确定性映射是函数最核心的基因也是后续所有应用场景的出发点。数学里的f(x)x²也好g(x)sin(x)也罢都在表达同一件事输入x输出一个由x唯一决定的数。1.2 函数的三大要素输入、输出、对应关系很多人学函数时只盯着f(x)这个记号忽略了函数其实由三个部分构成。输入域定义域哪些值允许被放进来。比如f(x)√xx就不能是负数f(x)1/xx就不能是0。现实中很多报错就是输入越界导致的。输出域值域所有可能的结果。f(x)x²的输出只能是大于等于零的数。对应法则从输入到输出的那条规则这是函数的灵魂。理解这三大要素要明白一个关键点对应法则才是函数的核心f(x)这个符号只是方便我们给它取名字、做运算。就好比你的名字不等于你这个人只是用来称呼你的代号。具体到实践里定义域的限制往往是最容易被忽略的。我见过很多初学者在写Python时执行math.sqrt(-1)直接报错这就是没有检查定义域也见过写代码时下标越界、分母为零本质上都是数据触碰到函数的定义域边界。反过来理解值域也有用你在Excel里用LOG函数输入负数照样返回#NUM!错误同样是因为输入集合不合法。1.3 从集合到图形函数图像说的事函数图像是中学数学里一个重要的可视化工具。为什么要把函数画出来因为图像能让你在几秒钟内看出函数的整体特征单调性递增还是递减、奇偶性、最大值最小值、变化趋势。我当年学函数时觉得画图很麻烦直到做数据分析才发现图像是人类理解数据规律的最快途径。这背后其实是一个通用方法论把抽象关系变成可视形式让模式自己跳出来。后来我在编程里用matplotlib画损失函数曲线、在Excel里插入折线图本质上都在干同一件事。如果你正在学数学类的函数我的建议是不要只盯着公式算值多动手画图。一个函数长什么样远比它能算几个数重要得多。2. 编程世界里的函数为什么每门语言都离不开它2.1 代码里的函数把重复劳动打包成工具数学里的函数是一条计算规则编程里的函数则更进一步它变成了一段可以反复调用的代码块。你写一段代码完成某个特定任务给它取个名字之后每次需要这个功能时只要喊名字就行不用重新写一遍。举个例子。你的程序里多次需要计算两个日期之间相差的天数难道每次都复制粘贴那十几行计算逻辑显然不合理。你定义一个函数GetDateDiff内部写好计算过程之后每次需要就调用它。这就是函数在编程中最基础也最重要的价值——消灭重复代码。这里有个关键差别要理解数学函数追求输入一个值返回一个值编程函数除了返回计算结果还可以做事情——读写文件、打印日志、发网络请求。这些行为叫作函数的副作用。设计良好代码时人们往往希望把计算和副作用分开因为纯计算的函数好测试、好复用不做多余事情、不依赖外部状态。这也是为什么现在前端领域这么推崇纯函数输入相同输出永远相同没有隐藏的坑。2.2 参数、返回值、作用域一次说清编程函数涉及几个绕不开的概念每一个都有对应的坑。参数是函数接收外部数据的入口。参数分为形参定义时写的占位名和实参调用时真正传入的值。传递方式有按值和按引用之分按值传递时函数拿到的是副本改它不影响原来的数据按引用传递时函数拿到的是地址改它会牵动原始数据。很多初学者犯的错就是把按引用传递当成按值在操作结果原数据被意外修改。返回值是函数交还结果的方式。有些语言里函数如果没有显式return会返回一个默认值比如Python返回None。这点很容易踩坑你以为函数执行完就自动返回结果实际拿到一个None后续继续用这个值做运算就崩了。作用域函数内部的变量是局部的出了函数就消失。函数外部定义的变量是全局的函数内部能不能访问要看语言规则。因此修改全局变量会带来隐患——多个函数同时操作同一个全局变量程序的状态就不可控了。这也可以看出为什么纯函数在多人协作的项目里那么重要它不依赖、不修改外部状态用起来安心。关于变量作用域我自己刚学C语言时犯过一个特别典型的错误在这里分享出来。我在main函数外面定义了一个全局变量sum然后在自定义函数里写sum sum i想累加结果发现不管调用多少次sum都只在函数内部变main里打印出来的值永远是0。我当时百思不得其解后来才明白除非在函数里明确声明要操作的是全局变量比如C语言里不要在函数内重新定义一个同名变量否则函数内部只会创建自己的局部变量。这类问题几乎每个学编程的人都会遇到理解了作用域这个词就全都通了。2.3 函数声明、定义、调用顺序问题别小看热搜词里有人搜函数声明这背后确实是个让新手头疼的顺序问题。在C、C、Java这类编译型语言里编译器从上往下读代码如果你在main里调用一个还没有定义的函数编译器会报错找不到这个函数。解决办法有两个要么把函数定义写在调用之前要么在文件开头先声明函数原型——告诉编译器这个函数存在我先打个招呼定义在后面。这个设计让新手困惑却有其道理。编译是单遍扫描的必须先知道函数的签名函数名、参数类型、返回类型才能正确编译调用它的地方。声明和定义分离还有一个好处可以把函数的具体实现藏起来只暴露接口给外部使用。你在写C语言头文件.h时里面放的就是声明而把实现放在.c文件里别人只需知道怎么调用不必关心内部细节。值得一提的还有main函数参数。很多人学C语言写int main()写了很久直到某天看到int main(int argc, char *argv[])才懵住。这两个参数就是系统给程序的输入argc表示命令行传进来几个参数argv是一个字符串数组装的每个参数的具体内容。用命令行运行程序时空格后面的内容会逐一分到argv里。理解这个机制你在配置脚本、调试工具时就不会被奇怪的行为搞晕了。2.4 箭头函数、函数模板、函数嵌套语法形态的多样性编程领域里函数演变出了各种语法形态热搜词里有几类值得专门说一下。箭头函数是JavaScript里非常流行的一种简洁写法。普通函数用function关键字箭头函数用把参数和函数体连起来。它有个关键特性不绑定自己的this而是继承外层作用域的this。这个特性让它在回调场景里非常好用却也经常坑到从其他语言转过来的人——在普通函数里用this和在箭头函数里用this指向完全不同。用一句话记忆箭头函数没有自己的主角光环它看到的是外面那层环境。函数模板是C里的概念。你写一个通用的排序函数如果只针对int数组写以后换成double数组又得复制一遍。函数模板允许你定义一套逻辑里面使用类型占位符调用时编译器根据传入参数自动推导出实际的类型。这就是泛型编程思想本质上是把类型也当成参数。理解函数模板就理解了为什么C的STL容器能同时装int、string、自定义类。函数嵌套定义和嵌套调用Python里特别常见。嵌套定义指的是在函数内部再定义一个函数这种内层函数可以用外层函数的局部变量形成闭包的基础嵌套调用则是指一个函数在执行过程中调用另一个函数这是最常见的程序流程控制方式。注意区分两个词嵌套定义是函数里面写函数嵌套调用是函数里面调函数。前者偏结构后者偏流程。2.5 内置函数和库函数别重复造轮子热搜里有人搜内置函数这其实是编程效率和数学建模的一个重要交叉点。每门语言都自带一批常用函数的实现直接调用即可。Python的内置函数长这样abs()取绝对值、len()取长度、sum()求和、max和min找最值。C语言则依赖标准库math.h里封装了sqrt()平方根、pow()幂运算、sin/cos三角函数。关于sizeof函数需要头文件这个问题其实sizeof是一个运算符而不是标准库函数它由编译器直接处理所以用sizeof不需要包含任何特定头文件。网上很多新手被误导跑去include一个并不存在的头文件自然编译报错。这个细节说明一个道理背函数库不是靠死记而是要理解哪些能力是语言原生自带的哪些是从库里引来的。判断一个功能该自己写还是用库函数标准很简单先查语言自带的标准库再查知名的第三方库。重复造轮子不但浪费时间还容易在边界条件上出错——库函数在无数真实项目中被踩过坑可靠性普遍比你自己写的高。但也要注意不同语言对函数名的命名风格差异很大从C切到Python时比如pow、floor这些名字在不同语言里的行为保持基本一致容易产生一些使用习惯上的混淆动手前先翻一下对应语言的官方文档最稳妥。3. 搜函数的人都在解决什么具体问题3.1 终端里的cmdlet、函数、脚本文件报错是怎么回事热搜词里高频出现无法将pnpm项识别为cmdlet、函数、脚本文件或可运行程序的名称类似的还有claude、git、make。很多人在Windows的PowerShell里第一次安装并使用某个命令行工具时就被这串词震住了里面的函数两个字尤其令人困惑——我装的是工具跟函数有什么关系这里要解释一下PowerShell的命令体系。PowerShell里有几种可执行的命令类型cmdlet内置的专用命令、函数用户通过function关键字定义的命令序列、脚本文件.ps1文件、可执行程序.exe或已注册命令。当你输入一个词时PowerShell会按顺序去查找这个名词到底是不是一个可识别的命令。如果四个类别里都找不到它就会报出那句话。无法将pnpm识别为cmdlet、函数、脚本文件或可运行程序的名称翻译成人话就是Windows不知道pnpm是什么东西压根没找到这个命令的入口。大多数情况下原因有几种没有真正安装pnpm、安装了但是没有把它所在目录加入系统的PATH环境变量、或者安装完成后没有重新打开终端。解决方法也很直接检查node.js和pnpm的安装目录手动把它添加到PATH里然后重启终端。如果你熟悉PowerShell可以在当前会话里临时把目录加进$env:Path然后立刻试试看。顺带一提这个报错是中文系统里PowerShell的专属文案如果你切到英文系统会看到The term pnpm is not recognized as the name of a cmdlet,function,script file,or operable program。理解它背后的命令查找机制以后不管哪个工具爆出同样的错误你都能一眼判断出问题出在没装好还是没进PATH。3.2 Excel里的函数表格世界的操作符Excel是函数这个词离普通用户最近的地方。搜索词里有excel函数应用效率手册regexextract函数这类词条说明大量办公场景里函数就是提效的核心武器。Excel函数和编程函数是一脉相承的它也是输入到输出的映射只是一套内置好的、用图形界面调用的函数库。VLOOKUP按条件在区域中查找、IF条件判断、SUM求和、FILTER筛选、REGEXEXTRACT正则表达式提取文本——本质上都是预定义的规则你把参数填进去它给你返回结果。我拿实际的表格举例。假设你有一张员工表需要在另一张表里根据工号查找对应的姓名。最直观的做法是肉眼找小表还行几千条数据就废了。这时就是用VLOOKUP的好时机VLOOKUP(A2, 员工表!A:B, 2, FALSE)含义是拿A2这个工号在员工表的A到B两列区域里精确查找返回区域内第2列也就是姓名的值。这个公式用一次胜过肉眼查几百行。使用Excel函数有几个容易被忽略的细节。第一参数顺序不能记错VLOOKUP的第三参数是返回第几列很多人在这个参数上栽跟头。第二FALSE和TRUE要分清FALSE表示精确匹配TRUE表示近似匹配。查工号这类内容一定要用FALSE否则可能返回错误的人名。第三函数的返回值区域不能乱选——VLOOKUP要求查找列在所选区域的第一列如果工号不在第一列查不出结果是正常的。如果你正在用Excel处理大量数据我把常用函数分成几类帮你建立框架查找引用类VLOOKUP、XLOOKUP、INDEX、MATCH、逻辑判断类IF、IFERROR、AND、OR、文本处理类LEFT、RIGHT、MID、TEXT、REGEXEXTRACT、统计汇总类SUMIF、COUNTIF、AVERAGEIF。办公效率提升的核心不是记忆几百个函数而是知道我需要什么效果该往哪个方向去查函数手册。3.3 从相位噪声到损失函数工程软件里的函数是封装好的专业模型热搜词里还有一些工程色彩很浓的条目比如相位噪声的互相关函数壁面函数。这些词里的函数既不是数学课本里的教学例子也不是Excel里的公式而是某个工程领域里长期沉淀下来的专业模型。以相位噪声为例振荡器输出的信号稳定性会受各种随机扰动影响。工程师们用功率谱密度来描述噪声在不同频率上的分布而互相关函数则是通过两个信号之间的相关性来提取有用信息、压制随机噪声的工具。在精密测量和通信系统里互相关运算几乎是标配因为它能把两个信号中共同的部分凸显出来、把不相关的噪声平均掉。这里的函数是一个数学运算过程的封装贯穿从理论到仪器API实现的整个过程。再比如CFD计算流体力学里的壁面函数它是对近壁区流动的一种经验模型封装。直接模拟湍流边界层非常耗计算量工程软件就把近壁区的行为用一个经验公式拟合好作为壁面函数嵌入求解器这样既保证了精度又大幅节省了算力。它本质上就是一个封装了大量工程经验的特殊函数接口。这类场景给我们的启发是函数作为概念工具在专业软件里往往承担着行业知识结晶的角色。你调用一个壁面函数、一个互相关函数并不需要自己重写那些数学推导而是在使用前辈们验证好的规则。理解这一点你在使用MATLAB、Ansys这类工程软件时就能读得懂、查得准——看到函数两个字先想清楚它封装的是哪一段经验、期望的输入和输出是什么。3.4 深度学习里的函数明星们搜索词里出现softmax函数、0-1损失函数、yolo损失函数、核函数说明AI领域的人们也在大量使用函数这个概念。softmax函数解决的问题是把一堆可以任意大的分数转换成一组和为1的概率分布。它的公式不复杂对每个输入取指数再除以所有指数之和。这会造成一个效果大的数对应的概率更大小的也不会完全归零从而实现软性地选出最可能的类别。分类神经网络最后一层往往接的就是它。损失函数则是衡量模型预测值和真实值差距的函数。0-1损失函数最简单直接预测对了损失为0预测错了损失为1。但它有个致命缺点——不可导梯度无法回传所以在现代深度学习里并不直接用。大家更常听到的是交叉熵损失和均方误差损失它们都是0-1损失的可导的近似版本。yolo这类目标检测模型其损失函数更是复合型的把坐标误差、置信度误差、分类误差都加到一起说到底也是要衡量预测和真实标注之间的差距只不过有多个头、多种指标。核函数则是在支持向量机SVM这类算法中用来做空间变换的。它的核心思想是原始数据在低维空间线性不可分但映射到高维空间后可能就线性可分了。核函数允许你不显式地做高维映射而是直接在高维空间里计算内积等价于隐式完成了变换。这也是搜索词里optdigits手写数字分类中svm核函数与参数的影响研究这个题目的由来——通过选择合适的核函数和参数能明显改善手写数字的分类效果。说到底这些函数从来没跳出我们第一小节讲的框架输入分数、预测值、样本对、输出概率、损失、相似度、对应法则softmax公式、损失定义、核函数公式。只是使用场景不同叫法不同而已。4. 进阶函数的几大套路从认识函数到设计函数4.1 回调函数把函数当参数传来传去回调函数是编程进阶路上第一个让人摸不着头脑的概念热搜里搜这个词的人不少。其实它的定义很直白你把一个函数作为参数传给另一个函数后者在某些时机回过头来调用你这个函数这就是回调。用生活场景类比你打电话给客服客服忙线你说等有空了给我回电话。这个回电话就是回调——你提前交代了一个动作系统在条件满足时自动执行。在JavaScript里事件监听就是典型的回调场景按钮的click事件绑定一个函数用户点击时浏览器调用这个函数。理解回调的关键是时机两个字。回调函数不是你主动调用的而是把控制权交给了调用者让它决定何时调用。因此写回调函数时要特别注意函数里用到的外部数据调用时是否已经就绪。异步回调的坑其实都藏在时机里。Python里也有回调函数的用法比如给某些库传一个函数作为处理函数处理完文件后自动调用。热搜里python回调函数被大量搜索说明这个概念确实需要解释。我用一句话概括回调就是一种反向调用是理解事件驱动、异步编程、高阶函数的基础。4.2 函数作为对象函数还能被传递、返回、赋值回调函数之所以可行是因为在JavaScript、Python这类语言里函数是一等公民——函数在语言中的地位和其他数据类型数字、字符串、对象一样可以赋值给变量、放进数组、作为参数传递、作为返回值返回。这跟C语言里的函数指针有些神似但一等公民做得更彻底。在JavaScript里你可以写const add (a, b) a b; // 函数赋值给变量 function apply(fn, x, y) { return fn(x, y); } // 函数作为参数 const result apply(add, 3, 5); // 输出8这种能力让生成函数的函数成为可能也就是高阶函数。比如Python的装饰器本质上就是一个接收函数、返回新函数的函数在保持原函数结构的同时给它加上额外行为日志、权限校验、计时等。理解函数是对象这个设定之后很多抽象设计就读得懂了。你在看C函数模板、JavaScript高阶函数、Python装饰器时绕来绕去其实都在玩同一个花样把函数本身当成数据放进代码的数据流里参与计算。这是函数这个概念在编程领域超越数学定义的一次重要飞跃。4.3 函数的接口本质碰撞检测、驱动导出到内核函数热搜词里还有几个看似毫不相关的词条碰撞检测函数、c# dll导出函数、kernel_read函数。它们的共性在于这些函数就像零件上一个精确设计好的插口负责对接外部系统。碰撞检测函数是游戏引擎或物理引擎里用来判断两个物体是否相交的接口。你传进去两个物体的位置和形状信息它返回是否碰撞、碰撞点在哪里。对游戏开发者而言这是引擎向外暴露的计算插口引擎内部可能用了GJK算法、SAT算法等复杂理论但对外只要提供好参数和返回值就足够了。c# dll导出函数则是一个典型的跨语言交互场景。你用C或C写一个动态库把某些函数标为导出然后让C#通过DllImport声明并调用它。这里的函数名、参数类型、调用约定都需要精确匹配因为两个不同语言运行时之间唯一的接口契约就是函数签名。用大写体标注函数名不是魔法它是一份被双方共同认可的接口协议。协议对上就能解析好协议对不上全局崩溃。kernel_read函数这类内核函数则是操作系统内核里用来读取文件内容的内部接口。它的函数指针可以通过某些手段拿到并调用是内核安全研究里常见的关注点。从概念上讲它依然是一个封装输入文件路径和缓冲区输出读到的字节数。这里我想强调一个通用视角当你把函数站到接口的高度去理解时任何看起来复杂的系统都变得有条理了。模块之间通过函数互相协作每一个函数名本质上就是一份契约——需要什么输入、给你什么输出、有什么副作用。你阅读代码、对接API、逆向二进制都是在解析这些契约。4.4 函数相关的经典坑与通用排查思路把热搜词里那些问题汇总一下会发现函数相关的典型问题总共就几类。第一类是环境类的问题比如终端不认某个命令、库文件找不到第二类是签名和协议类的问题参数类型不匹配、返回类型不一致、调用约定错误第三类是作用域和生命周期问题局部变量被外部引用、全局变量被意外修改、闭包捕获变量时机不对第四类是性能类问题函数调用开销、递归过深导致栈溢出。排查这些问题的通用思路我自己总结成一套口诀先看输入是否越界再看调用是否匹配然后查作用域与生命周期最后看有没有异步时序问题。十次有九次问题都出在这四个环节之一。这个排查顺序对于为什么函数调用返回NaN为什么回调里的变量不是预期的值为什么dll导出函数一调用就崩溃几乎全部适用。5. 一些学习函数的心得与建议5.1 函数学习最容易踩的心理坑学了这么多年函数看过大量初学者踩坑我想专门说几个心理层面的误区因为它们和数学能力、智商其实没什么关系。第一个误区是背公式不等于理解函数。数学函数、编程函数都一样公式和语法只是表达方式核心在于理解输入输出关系和边界条件。你背下VLOOKUP的语法却不知道第四个参数的含义背下softmax的公式却不理解为什么要取指数照样无法应对稍微变形的场景。第二个误区是把不同领域的函数混为一谈。数学函数追求纯粹性编程函数允许副作用Excel函数是固化的操作规则机器学习里的损失函数要考虑可导性。如果有人问你函数是什么请回答输入到输出的确定性映射这一最底层的骨架如果遇到某个具体的函数概念则要额外搞清楚它所在领域的附加约束。第三个误区是只看定义不练习。函数是极其依赖实操的概念读十遍理论都不如自己定义并调用一个函数来得扎实。编程函数尤其如此默写十遍箭头函数语法不如在浏览器里把console.log打印出来看一遍。5.2 从模仿到设计函数思维的三个阶段理解函数的路径我自己总结为三个阶段。第一阶段是使用别人的函数——调用内建函数、库函数、Excel函数重点是理解参数和返回值的关系。第二阶段是实现自己的函数——把一段流程封装成函数合理设计参数、返回值和命名重点是组织代码。第三阶段是把系统拆成函数的集合——面对一个复杂业务设计哪些函数存在、函数之间怎么协作、哪些逻辑放函数内、哪些放调用方重点是架构思维。多数人停留在第二阶段但要真正解决复杂问题第三阶段的缺失往往是瓶颈。举个例子一个订单系统里是写一个巨大的函数处理所有逻辑还是拆成验证库存、计算价格、更新余额几个小函数好的设计者会选择后者因为每个小函数职责单一、可测试、可复用。这种拆分系统的能力本质上就是把现实世界的流程抽象成函数的能力。5.3 面对一个新语言快速上手函数的清单最后分享一个我换语言时快速上手函数能力的自检清单屡试不爽。函数的定义语法关键字是function、def、还是fn函数体用花括号还是缩进参数传递机制按值还是按引用能否设默认参数返回值机制必须显式return还是自动返回最后一个表达式有没有多返回值匿名函数/箭头函数的写法lambda表达式怎么整函数在语言里是不是一等公民能否作为参数、返回值传递作用域规则函数内部能否访问外部变量用这份清单把一个新语言过一遍基本就知道怎么写、怎么调、怎么避坑了。语言里函数概念的底层结构都是相通的变化的多半只是表面语法和个别机制。这也是为什么我把函数这个问题拆得这么细原因就是它值得。写在最后如果你认真读到这儿会发现函数这个题目真的很特别。它横跨数学、编程、办公、工程、机器学习多个领域每个领域都把它用出了新花样但底子永远是那三个问题输入是什么、输出是什么、对应规则是什么。往后你再看到任何以函数结尾的名词不管是损失函数还是互相关函数先别慌用这句话去套一套基本都能套出个大概。我个人这些年最大的体会是函数不只是一个知识点更是一种看待世界的框架——把注意力集中在接口和边界上复杂的事情自然就有了清晰的结构。希望这篇文章能帮你建立这个框架至少在下一次看到无法将××识别为cmdlet、函数、脚本文件这类报错时能够心平气和地判断出它属于哪类问题再决定从哪里下手。
返回列表