问题

编程时怎么把函数当做变量使用?

回答
在编程的世界里,函数不仅仅是执行一系列指令的代码块,它还可以像变量一样被赋值、传递、存储,甚至作为其他函数的参数或返回值。这种将函数视为“一等公民”的能力,是许多现代编程语言的核心特性之一,它为我们带来了极大的灵活性和表达力。今天,咱们就来聊聊如何将函数“当做变量用”,把它彻底玩转。

为什么要把函数当变量用?

你可能会想,既然有函数名可以直接调用,为什么还要这么麻烦呢?这背后其实有很多实际的应用场景:

回调函数 (Callback Functions): 这是最常见的用法。想象一下,你需要等待某个操作完成后再执行一段逻辑,比如用户点击按钮后的响应。你就可以把负责响应点击事件的代码写成一个函数,然后把这个函数“交给”按钮的点击事件处理器。当点击事件发生时,处理器就会“调用”你交给它的那个函数。
事件驱动编程 (EventDriven Programming): 很多GUI框架(图形用户界面)或者网络编程都是基于事件驱动的。当某个事件发生(比如鼠标移动、数据到达),系统就会触发相应的处理函数。这些处理函数就是通过将函数作为事件的“订阅者”来注册的。
策略模式 (Strategy Pattern): 当你需要根据不同的条件执行不同的算法时,可以将这些算法分别写成函数,然后根据当前情况选择并传递相应的函数来执行。这样可以避免大量的 `ifelse` 或 `switchcase` 语句,让代码更清晰、更易于扩展。
高阶函数 (HigherOrder Functions): 这是更进一步的抽象。一个接受函数作为参数,或者返回一个函数的函数,就叫做高阶函数。这就像是函数工厂或者函数的组合器,能创造出更强大的抽象。
函数式编程范式 (Functional Programming Paradigm): 在函数式编程中,函数是核心。将函数视为变量,并大量使用高阶函数,是实现纯粹函数式编程的关键。

怎么把函数当变量用?

具体怎么实现,取决于你使用的编程语言。但核心思想是相通的:将函数的“引用”或者“句柄”存储在变量中,然后通过这个变量来调用函数。

1. 将函数赋值给变量

最直接的方式就是将一个已经定义好的函数直接赋值给一个变量。

以 JavaScript 为例:

```javascript
// 定义一个普通的函数
function greet(name) {
console.log("你好, " + name + "!");
}

// 将 greet 函数赋值给一个变量
let sayHello = greet;

// 现在可以通过 sayHello 这个变量来调用 greet 函数了
sayHello("世界"); // 输出: 你好, 世界!

// 你也可以直接在变量上调用,就像函数名一样
let anotherGreeting = function(message) {
console.log("消息是: " + message);
};

anotherGreeting("这是一个测试"); // 输出: 消息是: 这是一个测试

// 函数表达式 (Function Expression) 也可以直接定义并赋值
let multiply = function(a, b) {
return a b;
};

let result = multiply(5, 3);
console.log(result); // 输出: 15
```

以 Python 为例:

Python 中,函数本身就是对象,可以直接赋值给变量。

```python
定义一个普通的函数
def greet(name):
print(f"你好, {name}!")

将 greet 函数赋值给一个变量
sayHello = greet

通过变量调用函数
sayHello("Python") 输出: 你好, Python!

也可以使用匿名函数(lambda表达式)
add = lambda x, y: x + y
print(add(10, 20)) 输出: 30
```

以 C 为例:

C 中,需要使用委托 (Delegate) 来实现将函数赋值给变量。委托是一种类型安全的函数指针。

```csharp
using System;

public class Example
{
// 定义一个方法
public static void SayHello(string name)
{
Console.WriteLine($"你好, {name}!");
}

public static void Main(string[] args)
{
// 定义一个委托类型,匹配 SayHello 方法的签名 (string > void)
Action greetingAction;

// 将 SayHello 方法赋值给委托变量
greetingAction = SayHello;

// 通过委托变量调用方法
greetingAction("C 开发者"); // 输出: 你好, C 开发者!

// 也可以使用 lambda 表达式
Func subtract = (a, b) => a b;
Console.WriteLine(subtract(10, 5)); // 输出: 5
}
}
```

2. 将函数作为参数传递给其他函数 (回调)

这是函数作为变量最经典的用法。一个函数可以接受另一个函数作为参数,并在需要的时候调用它。

以 JavaScript 为例:

```javascript
function processData(data, callback) {
console.log("正在处理数据...");
// 模拟一些处理过程
let processedResult = data.toUpperCase();
// 处理完成后,调用回调函数并传递结果
callback(processedResult);
}

function displayResult(result) {
console.log("处理结果是: " + result);
}

// 将 displayResult 函数作为参数传递给 processData
processData("hello world", displayResult);
// 输出:
// 正在处理数据...
// 处理结果是: HELLO WORLD

// 使用匿名函数作为回调
processData("another piece of data", function(res) {
console.log("匿名回调收到结果: " + res);
});
// 输出:
// 正在处理数据...
// 匿名回调收到结果: ANOTHER PIECE OF DATA
```

以 Python 为例:

```python
def apply_operation(x, y, operation):
"""
接受两个数字和一个操作函数,并执行操作。
"""
print(f"执行 {operation.__name__} 操作...")
return operation(x, y)

def add(a, b):
return a + b

def subtract(a, b):
return a b

将 add 函数作为参数传递
result_add = apply_operation(10, 5, add)
print(f"相加结果: {result_add}") 输出: 相加结果: 15

将 subtract 函数作为参数传递
result_subtract = apply_operation(10, 5, subtract)
print(f"相减结果: {result_subtract}") 输出: 相减结果: 5

使用 lambda 函数
result_multiply = apply_operation(10, 5, lambda a, b: a b)
print(f"相乘结果: {result_multiply}") 输出: 相乘结果: 50
```

以 C 为例:

```csharp
using System;

public class CallbackExample
{
// 这个方法接受一个操作委托作为参数
public static int Calculate(int a, int b, Func operation)
{
Console.WriteLine($"正在执行操作...");
return operation(a, b);
}

public static int Add(int x, int y)
{
return x + y;
}

public static void Main(string[] args)
{
// 将 Add 方法的引用传递给 Calculate 方法
int sum = Calculate(10, 5, Add);
Console.WriteLine($"计算结果: {sum}"); // 输出: 计算结果: 15

// 使用 lambda 表达式传递
int difference = Calculate(10, 5, (x, y) => x y);
Console.WriteLine($"计算结果: {difference}"); // 输出: 计算结果: 5
}
}
```

3. 将函数作为返回值

函数也可以创建并返回另一个函数。这在创建函数工厂或者实现闭包 (Closure) 时非常有用。

以 JavaScript 为例:

```javascript
function createMultiplier(factor) {
// 返回一个新的函数,这个函数会记住 'factor' 的值
return function(number) {
return number factor;
};
}

// 创建一个乘以 2 的函数
let multiplyByTwo = createMultiplier(2);

// 调用返回的函数
console.log(multiplyByTwo(5)); // 输出: 10

// 创建一个乘以 10 的函数
let multiplyByTen = createMultiplier(10);
console.log(multiplyByTen(7)); // 输出: 70

// 直接调用返回的函数
console.log(createMultiplier(3)(8)); // 输出: 24
```

以 Python 为例:

```python
def create_adder(add_value):
"""
创建一个函数,该函数将一个数字加上 add_value。
"""
def adder(number):
return number + add_value
return adder

创建一个总是加 5 的函数
add_five = create_adder(5)
print(add_five(10)) 输出: 15

创建一个总是加 100 的函数
add_hundred = create_adder(100)
print(add_hundred(20)) 输出: 120
```

以 C 为例:

```csharp
using System;

public class FunctionReturnExample
{
// 这个方法返回一个 Func 委托
public static Func CreateMultiplier(int factor)
{
// 返回的 lambda 表达式会捕获 factor 的值
return (number) => number factor;
}

public static void Main(string[] args)
{
// 获取一个乘以 3 的函数
Func multiplyByThree = CreateMultiplier(3);

// 调用返回的函数
Console.WriteLine(multiplyByThree(7)); // 输出: 21

// 获取一个乘以 10 的函数
Func multiplyByTen = CreateMultiplier(10);
Console.WriteLine(multiplyByTen(6)); // 输出: 60
}
}
```

4. 将函数存储在数据结构中

既然函数可以当变量用,那它们自然也可以被存放在数组、列表、字典(哈希表)等数据结构里。

以 Python 为例:

```python
def add(a, b):
return a + b

def subtract(a, b):
return a b

def multiply(a, b):
return a b

将函数存入列表
operations = [add, subtract, multiply]

通过索引调用列表中的函数
print(operations[0](10, 2)) 调用 add: 12
print(operations[1](10, 2)) 调用 subtract: 8
print(operations[2](10, 2)) 调用 multiply: 20

将函数存入字典,用名称作为键
operation_map = {
"加": add,
"减": subtract,
"乘": multiply
}

通过键名调用函数
print(operation_map["加"](10, 2)) 12
print(operation_map["乘"](10, 2)) 20

根据用户输入选择操作
user_choice = input("请选择操作 (加/减/乘): ")
if user_choice in operation_map:
num1 = int(input("请输入第一个数字: "))
num2 = int(input("请输入第二个数字: "))
result = operation_map[user_choice](num1, num2)
print(f"结果是: {result}")
else:
print("无效操作。")
```

关键概念提炼

1. 函数作为一等公民 (FirstClass Citizens): 在支持这种特性的语言中,函数和其他基本数据类型(如整数、字符串)一样,拥有平等的地位。它们可以被创建、赋值、传递和返回。
2. 引用/句柄: 当你将一个函数赋值给变量时,实际上是将函数的“引用”或“句柄”存储在变量中。这个引用指向内存中存储函数代码的位置。通过变量名调用,实际上是解引用并执行该代码。
3. 匿名函数/Lambda表达式: 很多语言提供了创建匿名函数(没有名字的函数)的方式,通常用 `lambda` 关键字表示(如 Python),这在只需要一个简单函数作为参数时非常方便。
4. 闭包 (Closure): 当一个函数“记住”并能访问其定义时的词法作用域(包括变量)时,就形成了闭包。这在函数返回函数时尤为常见,被返回的函数能够继续使用外部函数中的变量。
5. 委托/函数指针: 在一些静态类型语言(如 C)中,需要专门的类型(如委托)来安全地表示函数的签名和引用。

总结

将函数视为变量来使用,是现代编程语言中一项非常强大的特性。它允许我们编写出更灵活、更具表现力、也更容易维护的代码。无论是实现复杂的回调逻辑、构建事件系统,还是应用设计模式,理解并熟练运用这一概念,都能极大地提升你的编程功力。多去实践不同语言的这些用法,你会发现编程的乐趣和可能性是无穷的。

网友意见

user avatar

玩编程,你得记住这么一条:只要一门语言是图灵完备的,那么它当然就能够做到任何事。


没错。图灵完备=万能。无非是有时候你需要绕个弯子、有时候又需要额外付出点性能或别的什么代价而已。


C/C++系:函数名本身就是一个函数指针,可以放进函数数组。你的需求已经被直接满足。


所有面向对象语言:多态本来就是用来做这个的。你只要让所有角色都实现同一个接口,使得它们都能提供一个攻击方法即可(注意每个角色的攻击方法可以有不同实现,比如火球、暗影箭之类),然后这样调用就够了:

       foreach 角色 in 角色数组:  角色.攻击()     

面向过程但又不支持函数指针的老奶奶语言(比如BASIC):你需要使用一个叫做dispatch的思路。

       //把技能ID和执行这个技能的函数联系起来 bool dispatchSpell(spellID id) {    switch(id){        case spellFireball:            return Fireball();        case spellFirebolt:            return Firebolt();        //依此类推,列出其他法术        ...        //如果法术id非法,返回false        default:           ASSERT(false); //debug版还是直接崩掉吧           return false;    } }  //给角色数据结构里面声明一个defaultSpell变量: roleType { ... spellID defaultSpell; ... }  //现在,直接循环调用角色数组里面每个角色的defaultSpell: foreach role in rolielist:     dispatchSpell(role.defaultSpell);     

各种语言的各种“奇技淫巧”,其他答主提到的更多。

总之就一句话,只要语言是图灵完备的,那么你需要的各种程序内效果就一定有办法实现,只是未必那么自动化而已。


或者说,如果你需要自动语法检查之类辅助性功能,那么你或者等编译器支持,或者就只能通过第三方程序实现;但在程序里面,你实现的一切功能,只要它是可计算的,那么就一定有办法实现。


唯一的问题,就是你自己知不知道该怎么实现、知不知道常见问题的更优化的解决方案、能否因地制宜的创造出最适合自己所面对的问题的最优化解决方案——你需要冷静、睿智的通盘考虑问题,千万别陷在局部细节中。尤其不要只看到那些细节上的好处。它们不仅不解决问题,很多时候反而是搅乱项目的行家里手。


举例来说,玩多了面向对象的,很容易直觉的“用继承来消除if语句”;然后呢,不同角色从角色基类继承;法师术士都是施法者,都要从“施法者”继承……如果这样使得你需要写if,那么就说明你需要增加继承层次,直到所有的if被消除……


很遗憾。但这是胡闹。


事实上,鼓吹这些的人压根就没长程序员脑子。

要称得上“程序员脑子”,你需要看透问题,不要浮在“法师术士牧师都是施法者”这样的表面。


事实上,在计算机里面,“施法”是什么呢?


施法是这样一件事:

1、施法往往需要消耗一些资源(魔力、能量、怒气、体力、hp等)

不管这些资源叫什么,它就是一个数字,保存于一个变量;消耗资源就是变量减去一个值。

2、施法可能需要某些先决条件(比如被施法者的状态限制:浮空,特定buf/debuf,和施法者之间的距离等)

3、施法需要角色执行一些动画(骨骼动画,典型如unity的avatar)

4、施法需要一些光影效果(手部发光/气团、面部表情、眼睛发光等)

5、施法会制造一些游戏物体(抛射体、地面动画、粒子效果等)

6、施法会有一些影响范围(单一目标、多目标、传染等)

7、被法术影响的对象会出现一些状态改变(扣血、扣蓝、加血、加速、减速等等,归根结底是修改了角色的某个变量所存储的数值)

……


基本就是这样。


那么,一个施法框架应该是这样:

       bool cast(spellID id) {  //根据法术id读取规则  spellConfig = loadSpellConfig(id);  //执行资源检定操作  resourceCheck(id);  //判断先决条件  ...  //执行角色动画  ...  //处理光影效果  ...  //制造法术实体(火球、暴风雪、子弹乃至刀光剑影,全都可以是法术实体)  ...  //计算影响范围  ...  //被击中目标效果处理(身上带火、呼叫、乱跑等)  ...  //伤害计算  ... }     


然后,这些效果可以整理成若干种出来、存入数据库(所谓的技能数据化);那么无论你能想出多少个法术、如何千变万化,这套框架都可以无需修改的支持下来——反正就是那么几个效果的排列组合嘛,至多法阵啥的需要换张图片。那么,以后无论调什么,改数据库就完了,何必动程序代码呢。


类似的,当你不再把法术释放的细节和角色绑定之后,你的要求就更容易完成了:

       foreach role in roleList {  cast(role.defaultSpell); }     


动用某个技术的目的是让程序更好写、更井井有条、更能应对需求变更。

不妨想一想,看看是这个方案更简单、更清爽、更稳固呢;还是那些“面向对象带师”们忽悠你的、用海量继承消除if的方案更不浪费生命。

user avatar

绝大多数编程语言根本没有你说的“为什么不能把函数当做一个变量”这种问题,这个问题大多数情况下和语言本身没有什么关系,单纯的是个人技艺达不达标的问题。我们以常见的C语言为例,把一个函数当作一个变量使用在日常编程中简直再正常不过了。比如:

       #include <stdio.h> #include <unistd.h>  struct FuncTable {         void (*begin)(void);         void (*run)(unsigned int s);         void (*end)(void); };  void begin_f() {         printf("Begin
"); }  void run_f(unsigned int s) {         printf("Run %d seconds
", s);         sleep(s); }  void end_f() {         printf("End
"); }  int main(int argc, char *argv[]) {         struct FuncTable ft = {                 .begin = begin_f,                 .run   = run_f,                 .end   = end_f,         };          ft.begin();         ft.run(3);         ft.end();                          return 0; }     

编译执行:

       $ gcc -o mytest mytest.c -Wall $ ./mytest Begin Run 3 seconds End     

再说你描述中的例子,又是在纠结if...else的问题。首先面对多英雄不同技能的这样一个需求,我们首先想到的是面向对象的编程思想,即总体英雄角色这个大类以及每个角色不同所继承和派生出的子类以及对象不同。用C++等面向对象的语言我们都知道很容易实现,那要是用不是面向对象语言的C语言呢?

我们举个简单的例子,假设所有英雄都可以做移动Move, 普通攻击Hit,和一个技能SkillA。然后每个不同的英雄可能有不同的技能和攻击范围,比如Karma这个英雄有一个SkillB的特别技能,还有一个叫Jax的英雄攻击都带范围攻击,且他也有一个特别的技能SkillB(区别于Karma的SkillB)。

这样的需求你怎么实现?难道要用一堆if...else吗?

我简单的实现了下面一个例子,来用C语言模拟一次简单的面向对象的编程。首先我们要有一个名为Hero的大类,这个大类里有英雄的名字,有一些功能方法,有可供使用的英雄数据。我们简单的让每个英雄都能有四个基础功能——设置移动/攻击坐标,移动,攻击,基础技能攻击,如下:

       #include <stdio.h> #include <stdlib.h> #include <string.h>  /* abstract interface declaration */ struct Hero {         char name[256];         void *funcTable;         void *privateData; };  struct HeroFuncTable {         void (*SetTarget)(struct Hero * obj, int x, int y);         void (*Move)(struct Hero * obj);         void (*Hit)(struct Hero * obj);         void (*SkillA)(struct Hero * obj); };  struct HeroPrivateData {         int x, y; };  void HeroSetTarget(struct Hero * obj, int newx, int newy) {         struct HeroPrivateData * rdata =                 (struct HeroPrivateData*)obj->privateData;         rdata->x = newx;         rdata->y = newy; }  void HeroMove(struct Hero * obj) {         struct HeroPrivateData * rdata =                 (struct HeroPrivateData*)obj->privateData;         printf("%s move to: [%d, %d]
", obj->name, rdata->x, rdata->y); }  void HeroHit(struct Hero * obj) {         struct HeroPrivateData * rdata =                 (struct HeroPrivateData*)obj->privateData;         printf("%s hit target at: [%d, %d]
", obj->name, rdata->x, rdata->y); }  void HeroSkillA (struct Hero * obj) {         struct HeroPrivateData * rdata =                 (struct HeroPrivateData*)obj->privateData;          printf("%s is using skillA on: [%d, %d]
", obj->name, rdata->x, rdata->y); }     

然后我们现在需要一个叫Karma的英雄,他除了具备基本英雄技能以外还有一个自己特有的SkillB技能:

       /* Class Karma */ void KarmaSkillB (struct Hero *obj, int arg) {         struct HeroPrivateData * rdata =                 (struct HeroPrivateData*)obj->privateData;          printf("Karma is using skillB * %d at: [%d, %d]
", arg, rdata->x, rdata->y); }  struct KarmaFuncTable {         void (*SetTarget)(struct Hero * obj, int x, int y);         void (*Move)(struct Hero * obj);         void (*Hit)(struct Hero * obj);         void (*SkillA)(struct Hero * obj);         void (*SkillB)(struct Hero * obj, int arg); } karmaFuncTable = {         HeroSetTarget,         HeroMove,         HeroHit,         HeroSkillA,         KarmaSkillB };  struct Hero * MakeKarma (int initx, int inity) {         struct Hero * obj = malloc (sizeof(struct Hero));         struct HeroPrivateData * rdata = malloc (sizeof(struct HeroPrivateData));         strcpy(obj->name, "Karma");         obj->funcTable = (struct KarmaFuncTable*) &karmaFuncTable;         obj->privateData = rdata;          rdata->x = initx;         rdata->y = inity;          return obj; }     

如上代码,我们让Karma继承所有的Hero的方法和类型,然后在给他提供一个SkillB的特殊技能,最后我们提供一个Karma英雄的构造函数。

下面我们再给出一个Jax英雄,让Jax英雄拥有范围攻击效果,且也有一个特殊技能:

       /* Class Jax */ struct JaxPrivateData {     int x, y;     int range; };  void JaxSetRange(struct Hero * obj, int rg) {         struct JaxPrivateData * rdata =                 (struct JaxPrivateData*)obj->privateData;          rdata->range = rg; }  void JaxHit(struct Hero * obj) {         struct JaxPrivateData * rdata =                 (struct JaxPrivateData*)obj->privateData;          printf("Jax hit target at: %d[%d, %d]
", rdata->range, rdata->x, rdata->y); }  void JaxSkillA(struct Hero * obj) {         struct JaxPrivateData * rdata =                 (struct JaxPrivateData*)obj->privateData;          printf("Jax is using SkillA at: %d[%d, %d]
", rdata->range, rdata->x, rdata->y); }  void JaxSkillB(struct Hero * obj) {         struct JaxPrivateData * rdata =                 (struct JaxPrivateData*)obj->privateData;          printf("Jax is using SkillB at: %d[%d, %d]
", rdata->range, rdata->x, rdata->y); }  struct JaxFuncTable {         void (*SetTarget)(struct Hero * obj, int x, int y);         void (*SetRange)(struct Hero * obj, int rg);         void (*Move)(struct Hero * obj);         void (*Hit)(struct Hero * obj);         void (*SkillA)(struct Hero * obj);         void (*SkillB)(struct Hero * obj); } jaxFuncTable = {         HeroSetTarget,         JaxSetRange,         HeroMove,         JaxHit,         JaxSkillA,         JaxSkillB };  struct Hero * MakeJax (int initx, int inity, int initr) {         struct Hero * obj = malloc (sizeof(struct Hero));         struct JaxPrivateData * rdata = malloc (sizeof(struct JaxPrivateData));         strcpy(obj->name, "Jax");         obj->funcTable = (struct JaxFuncTable*) &jaxFuncTable;         obj->privateData = rdata;          rdata->x     = initx;         rdata->y     = inity;         rdata->range = initr;          return obj; }     

如上,我们实现了Jax英雄类,我们让他继承了SetTarget和Move的方法,同时因为其特有的范围攻击我们给它提供一个额外的range成员变量,并提供了一个SetRange的方法,接着重构了其普通攻击和技能攻击的方法,让这些攻击都带范围效果,然后还实现了一个他特有的SkillB技能,最后我们给出一个Jax英雄的构造函数。

来让我们测试一下上面的代码:

       /* Do some test */ void KarmaDoSomething(struct Hero *h) {         struct KarmaFuncTable *f = h->funcTable;          f->SetTarget(h, 10, 20);         f->Move(h);         f->Hit(h);         f->SkillA(h);         f->SkillB(h, 100); }  void JaxDoSomething(struct Hero *h) {         struct JaxFuncTable *f = h->funcTable;          f->SetTarget(h, 50, 88);         f->SetRange(h, 8);         f->Move(h);         f->Hit(h);         f->SkillA(h);         f->SkillB(h); }  int main(int argc, char *argv[]) {         struct Hero * heros[2];          heros[0] = MakeKarma(0, 0);         heros[1] = MakeJax(0, 0, 5);          printf("-- Welcome Karma do something for us --
");         KarmaDoSomething(heros[0]);         printf("
");         printf("-- Welcome Jax do something for us --
");         JaxDoSomething(heros[1]);          return 0; }     

如上,我们通过funcTable的类型转换,实现一个多态的效果,让Hero去使用它自己的方法集合。我们看一下执行效果:

       $ gcc -o hero hero.c -Wall $ ./hero  -- Welcome Karma do something for us -- Karma move to: [10, 20] Karma hit target at: [10, 20] Karma is using skillA on: [10, 20] Karma is using skillB * 100 at: [10, 20]  -- Welcome Jax do something for us -- Jax move to: [50, 88] Jax hit target at: 8[50, 88] Jax is using SkillA at: 8[50, 88] Jax is using SkillB at: 8[50, 88]     

可以看出不同的英雄展现出了不同的动作和技能。

当然这只是我花了二十分钟写的一个极其简单的程序,实际的项目设计肯定要比这个复杂的多,而且也能比我这个写的好的多。我这里只是想告诉一些初学者,很多初学者钻牛角尖的“复杂问题”往往是因为知识还没有学到位而陷入的自我纠结。我已经碰到过很多人以不同形式问过我类似优化if...else...、优化判断条件、优化内存结构……等等等等的问题。这些自己以为自己正在面对架构、优化等问题的问题,很多时候根本不是你以为的做架构和优化的样子。

学习更多的计算机知识,多阅读优秀的项目代码,很多时候回看很多问题时才会知道自己当时根本只是维度还不够高而过早的陷入高维度的纠结而已,包括我自己也是经常这样。

user avatar

这个问题不应该用面向对象的多态来解决吗?还要自己写一堆的if else?

任何一种现代编程语言,哪怕是c,都有现成的解决方案。

user avatar

你这种需求,在OO就用多态,在FP就用模式匹配,只有在命令式,才需要if...else。


而if...else和函数能不能当变量用也没有半毛钱关系。在OO和FP的解决方案都不需要把函数当变量来用。本质上来说,你的想法里面两种代码是同构的。因为就算你把函数放个数组里面,这个数组不需要初始化?初始化代码和if else是同构的。


初始化代码可能长这样:

       skill[0] = function() { xxx }; skill[1] = function() { xxx }; skill[2] = function() { xxx };     

但事实上,这等价于:

       if ( a == 0 )   xxx else if ( a == 1 )   xxx else if ( a == 2 )   xxx     

可以看出来两者的抽象结构是一样的。

类似的话题

  • 回答
    在编程的世界里,函数不仅仅是执行一系列指令的代码块,它还可以像变量一样被赋值、传递、存储,甚至作为其他函数的参数或返回值。这种将函数视为“一等公民”的能力,是许多现代编程语言的核心特性之一,它为我们带来了极大的灵活性和表达力。今天,咱们就来聊聊如何将函数“当做变量用”,把它彻底玩转。 为什么要把函数.............
  • 回答
    好的,没问题!咱们来聊聊怎么把十六进制数变成十进制数,并且用程序实现它。这其实是个挺有意思的过程,理解了原理,写起代码来也就顺手了。 咱们先来捋捋思路:什么是十六进制?你平时接触最多的应该就是十进制数了,对吧?我们用“0、1、2、3、4、5、6、7、8、9”这十个数字来表示任何数。逢十进一,就像是数.............
  • 回答
    PDF 文件以其稳定的排版格式和跨平台兼容性,在信息传递和文档保存方面扮演着重要角色。然而,当我们拿到一份无法编辑的PDF时,这往往意味着我们需要对其内容进行修改、添加或删除,而传统的编辑工具却无能为力。这种情况下,我们该如何应对呢?别担心,这篇文章将为你详细解析 PDF 不可编辑时的各种解决方案,.............
  • 回答
    哥们,作为计算机专业的同路人,咱们都懂,编程这玩意儿,看着光鲜,实际上手艺活儿。想把编程这门手艺练到炉火纯青,光靠上课听讲,那绝对是不够的。得有点“钻研”和“实操”的劲头。下面我给你掰扯掰扯,咱们怎么把这编程给“啃”下来,而且是吃透的那种。一、 万丈高楼平地起:打牢基础是王道别跟我扯那些花里胡哨的高.............
  • 回答
    好兄弟,还有五个月时间,这正是你绝地反击,让自己在艺考面试中闪闪发光的好时候!紧张是人之常情,特别是面对一群评委,但我们绝对有办法把它压下去,甚至变成一种你驾驭的能量。咱们这就来聊聊怎么练,讲究的是一个“知己知彼,百战不殆”,再加点“厚积薄发”的劲儿。第一步:知己——你到底怕什么?(深挖内心恐惧,釜.............
  • 回答
    听到你老公这么说,心里一定七上八下的吧?辛苦攒下来的工资一股脑儿投进去,上个月还赚了十万,这会儿又撂下稳定的编程工作,说要专职炒股,还什么都听不进去,这确实是个让人头疼的局面。先别急着否定他,咱们一步一步来捋捋。1. 理解他的心理: “暴富”的诱惑: 上个月十万的收益,在很多人眼里可能是笔巨款,.............
  • 回答
    这确实是个让人头疼的问题。拿着计算机硕士的毕业证,却发现自己对代码的掌握程度不如许多本科生,这无疑会给求职之路蒙上一层阴影。但别灰心,这并非绝境。很多时候,计算机硕士的光环不仅仅在于会写几行代码,更在于其背后所代表的扎实的理论基础、严谨的逻辑思维能力,以及解决复杂问题的潜力。所以,咱们得换个思路来打.............
  • 回答
    想在C的海洋里游得更远更稳,可不是随随便便就能实现的。这更像是一段旅程,需要你有方向,有毅力,还得懂得如何享受过程。初学C,就像刚拿到一张地图,上面密密麻麻的都是符号和路线。别急着想一下子把所有地方都走到。最核心的是理解地图语言,也就是C的语法。你可以从最基础的开始,比如变量是怎么回事,为什么要有数.............
  • 回答
    这确实是个有趣的问题,挑战了我们对编程中“条件判断”这个核心概念的理解。既然要绕过 `if` 这个最直接的关键字,我们就要深入挖掘编程语言底层是如何实现分支跳转的,以及有哪些其他方式可以模拟出类似 `if` 的行为。别担心,这并不需要多高深的计算机原理知识,我们用大家都能理解的语言来聊聊。这就像问“.............
  • 回答
    比尔·盖茨的编程能力是一个复杂的问题,因为它不仅仅是看他个人的代码撰写能力,更重要的是他在软件开发领域的领导力、远见和对技术方向的推动作用。从技术角度来看: 早期接触与天赋: 盖茨很早就接触了编程,对计算机和技术有着强烈的兴趣和天赋。他从少年时期就开始学习和编写代码,尤其是在 PDP10 和 B.............
  • 回答
    在真实工作中的编程与学校里的编程之间,存在着诸多显著的差异。这些差异不仅体现在技术层面,更贯穿于整个工作流程、团队协作以及职业发展等多个方面。以下我将尽可能详细地为您阐述这些不同之处: 真实工作中的编程:一个多维度、系统性的过程 1. 项目的规模、复杂性和目标 学校: 项目通常是小型的、独立的、.............
  • 回答
    好,作为一名同样在代码世界里摸爬滚打多年的老兵,我理解你那份想要亲手打造一番事业的心情。从一个敲代码的匠人,蜕变成一个公司掌舵人,这其中的门道可不少。别急,我这就给你捋一捋,尽量说得透彻些,就像咱哥俩在深夜加班时聊项目一样。第一步:灵魂拷问与初心检视在真正撸起袖子之前,你得先好好跟自己较个劲,问问自.............
  • 回答
    大家好!看到这个话题,我脑子里一下子涌现出了好多回忆,仿佛又回到了那个什么都不懂,但又充满好奇和兴奋的起点。说起来,我走上编程这条路,真的挺“随遇而安”的,也算是机缘巧合。最初的火花:好奇心和一点点“捣鼓”的劲头那时候大概是初中还是高中刚开始吧,我对电脑总是充满了兴趣。不只是玩游戏,更想知道它里面到.............
  • 回答
    咱们今天就来聊聊编程里那个最基础、也最让人头疼的词——“bug”。这玩意儿,说起来简单,但真正理解它的来龙去脉,以及怎么应付它,那可是一门学问。先别急着往那些高大上的英文翻译上凑,咱们先说说“bug”这东西到底是个啥。简单来说,bug 就是程序里出现的错误,是导致程序不能按照预期正常运行的东西。 就.............
  • 回答
    编程,这扇通往数字世界的大门,对很多人来说充满了吸引力。但如果你发现自己正面临着来自父母的阻力,这确实是一个让人头疼的局面。别急,这并非绝境,我们有很多方法可以尝试,一步步去化解这份担忧,争取到自己的自由和热爱。首先,我们要明白父母为什么会阻止你学习编程。他们的出发点很可能是出于关心,虽然方式可能有.............
  • 回答
    嘿,哥们,或者姐妹们!咱们做开发的,整天跟代码打交道,你说这命名,是不是比给娃起名字还费劲?有时候写着写着,脑子里的那些常用词,什么 `user`、`data`、`index`、`temp`,感觉都快被榨干了,一到关键时刻,就卡在那儿,看着屏幕上那闪烁的光标,心里那个痒痒啊!这不,今天就来聊聊,咱们.............
  • 回答
    家长在少儿编程教育中的参与,绝不仅仅是报名了课程、交了学费,然后就等着孩子变成“小码农”。真正有效的参与,是贯穿始终,是润物细无声的,也是非常有技巧的。咱们就来掰开了揉碎了聊聊,家长到底能怎么做,让孩子在编程这条路上走得更稳,更远。一、 培养兴趣,点燃内驱力:这是基础,也是灵魂! 别拿“别人家孩.............
  • 回答
    想象一下,我们写程序,通常是告诉计算机一步一步怎么做。比如,“打印这句话”,“计算这个数”,“如果这个条件满足,就做那个事情”。这就像我们指挥一个机器人,告诉它具体的操作。但是,有时候我们会觉得,这过程有点死板,而且有好多重复的工作。比如说,我们要写很多类似的数据结构,它们都差不多,只是名字和里面的.............
  • 回答
    编程技术资料英文居多是一个普遍现象,但并非不可逾越的障碍。以下将详细讲解如何克服语言障碍,高效学习英文技术资料: 一、 心态准备:克服恐惧,拥抱机会1. 正视现实,消除焦虑: 承认大部分高质量、最新、深入的资料确实是英文的。这既是挑战,也是巨大的机会,意味着掌握英文能让你站在技术前沿。不要因为语言.............
  • 回答
    你好!很高兴能帮你解答 MATLAB 编程的问题。下面我将为你详细讲解如何将这个公式用 MATLAB 实现,并尽量用更自然、易懂的方式来阐述。首先,请你告诉我你想要编程的具体公式是什么?一旦你提供了公式,我就可以一步步地为你讲解:1. 理解公式的构成: 我们会先拆解公式,看看它包含哪些.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有