使用闭包使用函数式编程范式构建对象
闭包
闭包是由一组不同的变量范围规则产生的功能模式。在 C 和许多其他语言中,变量仅在定义它的代码块中有效。
...
while (i < 1) {
int c = 0;
//c is valid here
...
}
//c is invalid here
因为变量只在括号内有效,它的生命周期由屏幕上的文本决定,所以我们称之为词法作用域。
另一种类型的作用域——动态作用域——允许我们在定义函数的上下文中捕获变量。代码对局部变量的访问取决于定义上下文,而不是纯文本。
function createCountingClosure() {
let i = 0;
//i is valid here
return function() {
//i is STILL valid here, even after the outer function returns
//this anonymous function captures local variables for its scope
i = i + 1;
return i;
}
}
let inc = createCountingClosure();
// the inc function keeps the variable i around and can manpulate it
// even after the calling function has returned!
inc() // 1
inc() // 2
inc() // 3
再次在计划中(球拍)
(define createCountingClosure
(lambda ()
(let ((i 0))
;i is valid here
(lambda ()
;i is STILL valid here, even after the outer function returns
;this lambda function captures local variables for its scope
(set! i (+ 1 i))
i))))
(define inc (createCountingClosure))
; the inc function keeps the variable i around and can manpulate it
; even after the calling function has returned!
(inc) ; 1
(inc) ; 2
(inc) ; 3
以这种方式返回另一个函数的函数称为闭包。它打包(关闭)当前作用域的局部变量,并允许返回的函数有效访问它们,即使在调用函数返回后也是如此。
如果我们将局部变量视为属性,将返回的函数视为方法,将外部函数视为构造函数,那么这些闭包几乎看起来像对象!我们实际上可以使用对象获得与计数闭包相同的行为。
class countObject {
private int i = 0;
public int inc() {
i += 1;
return i;
}
static void Main(string[] args) {
countObject counter = new countObject();
counter.inc(); // 1
counter.inc(); // 2
counter.inc(); // 3
}
}
构建功能堆栈类
堆栈是一种数据结构,可以很容易地用具有对列表操作的方法的类来表示。我将继续使用 c# 作为我们的面向对象语言。 C# 的集合库中已经有一个堆栈,但我们将在这里使用数组列表滚动我们自己的堆栈。
class MainClass {
public static void Main(string[] args) {
Stack stack = new Stack();
stack.print(); // []
stack.push(2);
stack.print(); // [2]
stack.push(3);
stack.print(); // [2 3]
stack.peek();
stack.print(); // [2 3]
stack.pop();
stack.print(); // [2]
stack.pop();
stack.print(); // []
Console.Read();
}
}
class Stack {
private List list = new List();
public void push(T elem) {
list.Add(elem);
}
public T pop() {
var ret = list[list.Count - 1];
list.RemoveAt(list.Count - 1);
return ret;
}
public T peek() {
return list[list.Count - 1];
}
public void print() {
Console.Write("[");
foreach (T elem in list) {
Console.Write(elem + " ");
}
Console.Write("\b]\r
");
}
public int count() {
return list.Count;
}
}
这是一个非常简单的类,并没有涵盖所有错误情况,但它可以作为概念证明。让我们在功能上实现相同的概念。首先,我们将使用纯函数实现来实现方案,然后使用 javascript,我们可以使用一些技巧使其像类一样工作。
(define stackClosure
(lambda ()
(let ((stack '()))
(lambda (opp [arg 0])
(cond
((eq? opp "push") (set! stack (cons arg stack)))
((eq? opp "peek") (car stack))
((eq? opp "pop") (let ((ret (car stack))) (set! stack (cdr stack)) ret))
((eq? opp "print") (display stack) (newline))
((eq? opp "count") (length stack)))))))
(define stack (stackClosure))
(stack "print") ; ()
(stack "push" 2)
(stack "print") ; (2)
(stack "push" 3)
(stack "print") ; (3 2)
(stack "peek")
(stack "print") ; (3 2)
(stack "pop")
(stack "print") ; (2)
(stack "pop")
(stack "print") ; ()
首先我们在外层函数中创建一个列表,内层函数会根据需要保留对这个列表的引用。内部函数有两个参数:一个操作和一个可选数字。该操作告诉函数调用哪个方法,如果该方法(在本例中为 push)需要一个参数,您可以给它一个参数。这与我们的 c# 类的用法和功能非常相似。
function stackClosure() {
let stack = []
return {
"push": function(i) {stack.push(i)},
"pop": function() {return stack.pop()},
"peek": function() {return stack[stack.length-1]},
"print": function() {console.log(stack)},
"count": function() {return stack.length}
}
}
let stack = stackClosure()
stack.print() // []
stack.push(2)
stack.print() // [ 2 ]
stack.push(3)
stack.print() // [ 2 3 ]
stack.peek()
stack.print() // [ 2 3 ]
stack.pop()
stack.print() // [ 2 ]
stack.pop()
stack.print() // []
javascript 方法类似,只是我们可以访问 javascript 对象。这些允许我们创建一组键/值对并使用点符号来访问它们。使用堆栈闭包的符号与使用类的方式完全相同,只是在功能上定义。 javascript 中的类实际上只是像这样的闭包的语法糖。
仅使用函数和一些不同的范围规则,我们就可以创建类和对象。我认为很多时候我们首先被教导的是面向对象编程,并且是“最好的”风格。然而,它经常被用作拐杖,有时功能或程序的方法可以更简单、更强大。在我们盲目地将课程和无休止的花哨抽象层扔给问题之前,思考这个问题很重要。
关注七爪网,获取更多APP/小程序/网站源码资源!
留言与评论(共有 0 条评论) “” |