1
本文介绍了 18 个比较冷门的编程概念,并通过示例代码帮助读者理解这些概念,同时强调了它们在编程实践中的重要性。这些概念虽然不为人所知,但对提升编码技能和思维方式具有重要意义。原文:18 Programming Concepts You’ve Never Heard of (But Should!)

作为开发者,我们需要不断学习新的术语、技术和最佳实践。但是,如果有人告诉你还有许多你甚至不知道的概念,而且这些概念可能会改变你编码和思考的方式,你会怎么想?无论你是初学者还是经验丰富的开发者,这 18 个冷门概念都将帮助你提升水平。本文就会带你深入了解这些概念,示例代码将基于 JS 和 Python。

1. Thunk:善用拖延症

你有过把事情拖到最后一刻才做的经历吗?thunk 正是如此,它将函数的执行推迟到绝对必要的时候

想象一下,有一项计算成本很高的任务,但暂时不确定是否需要计算结果。与其提前计算,不如用 thunk 将其封装起来。

const thunk = () => 5 + 10;
console.log(thunk()); // 只在调用时进行计算
2. Monad(单子):代码的安全网

Monad 就像是函数的安全网,帮你实现链式操作并处理意外行为(如错误或空值),而不会导致整个程序崩溃。如果你正在处理异步数据,可能已经在不知不觉中用到了 monad!

new Promise((resolve) => resolve(5))
    .then(value => console.log(value)) // 输出: 5

Monad 看起来很神秘,但可以把它们想象成沿着管道安全传递数据的容器

3. Closure(闭包):代码记忆

试想一下,如果过去的自己能给未来的自己留下一段回忆会怎么样,这正是 closure(闭包)的作用,它允许函数 "记住" 变量创建时候的上下文。

def counter():
    count = 0

    def _():
        nonlocal count
        count += 1
        return count
    return _

increment = counter()
print(increment())
print(increment())
print(increment())

#1
#2
#3
4. Memoization:让我们提高效率,好吗?

有没有反复问同一个问题的经历?很烦人吧?计算机也是这么想的。Memoization 通过存储结果解决了这个问题,这样就不必重复进行昂贵的计算。

def fib(n, memo={}):
    if n in memo:
        return memo[n]
    if n < 2:
        return n
    memo[n] = fib(n - 1, memo) + fib(n - 2, memo)
    return memo[n]

fib(100)

#354224848179261915075
5. Continuation:未来掌握在自己手中

continuation 就像在代码中按下 "暂停" 键,并将稍后执行下一步,这是许多异步系统背后的秘诀。

function asyncTask(callback) {
    setTimeout(() => {
        console.log("Task complete");
        callback();
    }, 1000);
}
asyncTask(() => console.log("Next step"));

1 秒后,箭头内的代码 () => { console.log("Task complete"); callback(); } 将被执行。

处理过异步编程吗?写过最后的回调函数吧?它就是一个 continuation,是将在未来发生的事情!

6. Idempotence(幂等性):无论做多少次,结果都和第一次一样

在编程过程中,有些操作无论执行多少次,其行为方式都是一样的。这就是幂等性 -- 无论调用 API 一次还是 100 次,结果都是一样的。

GET /user/123  HTTP/1.1

如果一直调用 GET /user/123,得到的永远是同一个用户。API 的世界依赖于这一原则,它让一切都具有可预测性和安全性。

7. Quine(奎因):自我复制的程序

quine 是一种聪明的代码,能输出自己的源代码,就像照镜子时看到……嗯,代码正盯着你看。

s = 's = {!r}; print(s.format(s))'; print(s.format(s))

#输出 => s = 's = {!r}; print(s.format(s))'; print(s.format(s))
8. Zipper:超级结构化导航

在不造成混乱的情况下浏览和修改数据结构是一件棘手的事情,直到出现了 zipper,它可以帮助我们高效遍历和修改结构(如树),而不会破坏原有结构。

class Zipper:
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def move_left(self):
        return Zipper(self.left[:-1], [self.left[-1]] + self.right)

    def move_right(self):
        return Zipper(self.left + [self.right[0]], self.right[1:])

Zipper 是复杂数据结构的导航指南针

9. Functor(函子):友好的映射

functor 是一种可以映射函数的东西,可以在不接触容器本身的情况下,对容器中的每个数据进行转换。它不仅仅是函数式编程,更具备强大的组合能力。

numbers = [1, 2, 3]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # [1, 4, 9]

Python 中的 list 是函数,允许我们在不改变原始结构的情况下进行映射操作。

10. Tail Call Optimization(尾调用优化):不会造成栈溢出的无限递归

尾调用优化(TCO)让我们在编写递归函数时不必担心内存不足。如果函数的最后一个操作是调用自身,TCO 可以重复使用当前函数的栈帧。

def factorial(n, acc=1):
    if n == 0:
        return acc
    return factorial(n - 1, acc * n)

注:以上示例只显示了 TCO 的结构,但请记住 Python 本身并不支持 TCO。

请查阅参考资料:

Tail Recursion Elimination (2009–04–22)

Final Words on Tail Calls (2009–04–27)

11. Currying(柯里化):请一次只做一件事!

柯里化将一个接受多个参数的函数分解成一系列函数,每个函数只有一个参数。

def add(x):
    def inner(y):
        return x + y
    return inner

add_five = add(5)
print(add_five(3))  # 8

柯里化是获得灵活性的关键 -- 想要应用部分参数?柯里化可以做到。

还可以基于 partial 进行函数柯里化。

from functools import partial

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

two_adder = partial(adder, 2)
two_adder(10)

#12
12. Spectre & Meltdown:困扰你的 CPU

你可能听说过 Spectre 和 Meltdown 这两个震惊世界的臭名昭著的漏洞,它们利用了现代处理器优化性能的方式,泄露了敏感数据。

if (untrusted_input < array_size) {
    temp = array[untrusted_input * 512];
}

单纯追求速度可能会让事情更糟,投机取巧的执行可能会加快代码的执行,但有时泄露的信息会超出我们的想象!

13. Homomorphism(同态):保留结构

Homomorphism(同态)是个花哨的词,指在两个代数系统之间保留结构的函数。在数学中是很重要的概念,但在编程中转换数据时也很重要。(最重要的函数式编程概念之一)

numbers = [1, 2, 3, 4]
def square(x):
    return x * x
squared_numbers = list(map(square, numbers))
print(squared_numbers)  

# Output: [1, 4, 9, 16]
14. Lazy Evaluation(惰性求值):只在需要时

惰性求值会将表达式的求值延迟到需要时才进行,对于提高大型数据集或计算程序的性能非常有用。

def lazy_range(n):
    i = 0
    while i < n:
        yield i
        i += 1

for num in lazy_range(5):
    print(num)

生成器只在需要时才计算数值,从而节省了内存和处理时间。

thunk 和惰性求值生成器 😫 有何区别?

  • 惰性求值是一种延迟求值表达式的策略,直到实际需要时才进行计算。它可以避免不必要的计算,从而帮助优化性能,尤其是在使用无限数据结构或处理昂贵的操作时。
  • Thunk 是一种用于实现惰性求值的特定技术,本质上是一个没有参数的函数,封装了计算或表达式,计算会被延迟到 Thunk 被明确调用时执行。

Effortless Efficiency: The Art of Lazy Evaluation

15. Canonicalization(规格化):将数据标准化

Canonicalization(规格化)是将数据转换为标准或规范化形式的过程,通常用于数据库和 URL,以确保一致性。

http://example.com
https://example.com
http://www.example.com

规格化可确保所有版本都指向一个唯一首选版本。

16. Side Effect(副作用):超出预期

当函数或表达式修改了其本地环境之外的某些状态时,就会产生副作用,例如改变全局变量、I/O 操作或修改输入参数。

def add_to_list(value, lst=[]):
    lst.append(value)
    return lst

print(add_to_list(1))  # [1]
print(add_to_list(2))  # [1, 2] (unexpected!)

本例中的函数修改了默认参数,导致了意想不到的副作用。

17. Hoisting:将声明移至顶部

在 JavaScript 中,hoisting 会在编译阶段将变量和函数声明移到其作用域的顶部。

  console.log(x);  // 未定义
  var x = 5;

即使 x 是在 console.log 之后声明,JavaScript 也会将声明提升到顶部,使 x 在定义之前就可以访问。

18. Monoid(单态):以一致的方式组合数据

monoid (单态)是一种代数结构,具有二元运算和一个标识元素,允许以关联方式组合数据。

result = ''.join(['a', 'b', 'c'])
print(result)  # 'abc'

这里的字符串连接构成了一个以 '' 为标识元素、以 + 为二进制运算的单元。

结论

这些高级编程术语初看起来可能很深奥,但理解了它们可以极大改进编写代码的方法。无论是优化性能、安全性还是可读性,这些概念可以是强大的工具,帮助我们以优雅的方式解决复杂问题。


你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!

本文由mdnice多平台发布


俞凡
27 声望21 粉丝

你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起...