用Python实现编程练习Kata-FizzBuzzWhizz

Functional programming leads to deep insights into the nature of computation. – Martin Odersky

近日软件匠艺小组的朋友用函数式编程的方式重新实现了一个古老的Kata练习题:FizzBuzzWhizz,用了很多种语言及其特性,非常精妙。这是一个软件匠艺的文艺复兴时代。
原帖见https://codingstyle.cn/topics/100

题目

FizzBuzzWhizz详细描述请自行查阅相关资料。此处以3, 5, 7为例,形式化地描述一下问题。

r1
+ times(3) -> Fizz
+ times(5) -> Buzz
+ times(7) -> Whizz
r2
+ times(3) && times(5) && times(7) -> FizzBuzzWhizz
+ times(3) && times(5) -> FizzBuzz
+ times(3) && times(7) -> FizzWhizz
+ times(5) && times(7) -> BuzzWhizz
r3
+ contains(3) -> Fizz
+ the priority of contains(3) is highest
rd
+ others -> others

Python实现

借助原帖的测试用例,我用python语言也练习了一把。本来想引入Pipe库用管道式的方式写,可能会取得一些方便,但想想还是用原生的语法来做吧。

继续阅读 More

函数式Python中的Pipe与itertools

可迭代器(iterable),不仅限于list/str等,还包括任何包含有yield关键字的函数,后者未必有规律的迭代特征。标准库中的itertools包提供了更加灵活的产生迭代器的工具,这些工具的输入大都是已有的迭代器函数的封装,并且itertools给出的函数都是针对广义迭代器而言。而len()等函数是针对狭义迭代器,即sequence(i.e. str, list, tuple)而言的。
以内置函数range()为例,执行结果会是一次性计算好整个序列。这对于很长的序列来说会比较耗时,甚至带来性能问题。因而,python还提供了内置函数,提供了惰性求值版本,那就是xrange()。它利用yield特性,第一次执行时仅仅返回迭代器,不到用时是不会求值的。
实际上,itertools提供的函数都是惰性的,并且给原内置函数都重写了惰性版本。如imap()对于内置的map()

扩展库Pipe则对内置函数和部分itertools进行了封装,提供了类似unix bash下的管道式调用风格,更接近人类从左到右的阅读习惯,使得代码更加优雅。其他动态语言,如ruby, c#-lambda java8-lambda也都提供了类似的链式调用形式。
另外,也提供了@Pipe装饰器,可以非常方便地扩展出自己的管道函数,或者继续封装其他itertools中的有用函数。

这里是Pipe官方给出的例子,用管道函数式编程解出https://projecteuler.net/中的三道题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Find the sum of all the multiples of 3 or 5 below 1000.
euler1 = (itertools.count() | select(lambda x: x * 3) | take_while(lambda x: x < 1000) | add) \
+ (itertools.count() | select(lambda x: x * 5) | take_while(lambda x: x < 1000) | add) \
- (itertools.count() | select(lambda x: x * 15) | take_while(lambda x: x < 1000) | add)
assert euler1 == 233168

# Find the sum of all the even-valued terms in Fibonacci which do not exceed four million.
euler2 = fib() | where(lambda x: x % 2 == 0) | take_while(lambda x: x < 4000000) | add
assert euler2 == 4613732

# Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
square = lambda x: x * x
euler6 = square(itertools.count(1) | take(100) | add) - (itertools.count(1) | take(100) | select(square) | add)
assert euler6 == 25164150

注意:所有惰性求值的迭代器,都是只能求值一次的,如果再次求值会什么也得不到,因为yield堆栈已经走到底,无法回头。因此,当要对惰性迭代器重复使用时,必须故意地提前将其求值展开,或者利用itertools.tee来克隆一个迭代器。

继续阅读 More

Python函数式编程

函数式编程

如果程序中的函数仅接受输入并产生输出,即输出只依赖于输入,内部数据不可变,避免保存程序状态,用同样的输入值反复调用可以得到相同的结果,那么这种编程范式就称为函数式编程(Functional Programming,简称FP,又称泛函编程)

这种风格也称声明式编程(Declarative Programming),与之相对的是指令式编程(Imperative Programming),后者中的对象会不断修改自身状态。函数式编程强调程序的执行结果比执行过程更重要,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

函数编程语言最重要的基础是λ演算(lambda calculus),函数可以像数值一样被赋值于变量,还可以作为其他函数的输入(引数)和输出(传出值)进行传递。

函数式编程历史悠久,最古老的例子莫过于1958年被创造出来的LISP了。而随着程序结构复杂,面向对象编程大行其道。近年来,简洁而且特别适合计算任务的函数式编程又重新崛起,不仅仅是纯粹的函数式语言如Haskell、Clojure、Elixir等,各种流行语言javascripts、python、Objective-C、C#、Swift甚至Java都纷纷吸收函数式编程的部分形式。而且,不仅仅是计算任务,近年还出现了用FP编写的UI应用程序,如LightTable等。

Paul Graham在《黑客与画家》一书中写道:同样功能的程序,极端情况下,Lisp代码的长度可能是C代码的二十分之一。

本文作者@申导 主要采用Python语言为例,是因为它虽然不是纯粹的FP,但Python能够胜任各种编程形式,简洁优雅,通俗易懂,语法接近于Java/C++,特别适合从主流语言转过来的学习者。

继续阅读 More

Apple最新Swift编程语言之闭包

Swift作为苹果为开发者最新发布的编程语言,受到热烈的追捧。与2014世界杯同热。

作为一门动态语言,吸收了大量Python语言的特性和语法(我喜欢Python),当然也有些ruby、js和C#的痕迹,同时保持了对原objective-C库的兼容。

iBook推出了教程,中文翻译也完成了。

大家需要下载最新xcode,里面有交互式的playground可以用来学习swift。为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。 打开Playground

你需要知道Swift语言闭包函数 ()->()

定义一个函数

你可以用func关键字来定义函数。函数可以接收和返回0个、1个或多个参数(tuples列表)。返回类型在->符号后面。

1
2
3
func jediGreet(name: String, ability: String) -> (farewell: String, mayTheForceBeWithYou: String) {
return ("Good bye, \(name).", " May the \(ability) be with you.")
}

调用函数

1
2
3
4
let retValue = jediGreet("old friend", "Force")
println(retValue)
println(retValue.farewell)
println(retValue.mayTheForceBeWithYou)

函数类型

1
func sum(x: Int, y: Int) -> (result: Int) { return x + y }

上述函数的函数类型为 (Int, Int) -> (Int)
函数类型可以用于嵌套函数的参数类型或者返回类型。

传递和返回函数

下列函数将另一个函数作为结果返回,可以用于赋值给变量及调用。

1
2
3
4
5
6
7
8
func jediTrainer () -> ((String, Int) -> String) {
func train(name: String, times: Int) -> (String) {
return "\(name) has been trained in the Force \(times) times"
}
return train
}
let train = jediTrainer()
train("Obi Wan", 3)

可变入参函数

可变入参函数带有可变数量的入参(表示为参数类型后的...),其内容可作为数组来访问。

1
2
3
4
5
6
func jediBladeColor (colors: String...) -> () {
for color in colors {
println("\(color)")
}
}
jediBladeColor("red","green")

定义一个闭包

闭包被置于花括号{}中,且定义为()->()类型的函数。其中->分隔了入参与返回类型,其后的in关键字分隔了闭包头与闭包体。

1
2
3
{ (params) -> returnType in
statements
}

一个例子,map函数应用于数组。

1
2
3
4
5
let padawans = ["Knox", "Avitla", "Mennaus"]
padawans.map({
(padawan: String) -> String in
"\(padawan) has been trained!"
})

类型已知的闭包

当闭包的入参类型已知时,可以这样写:

1
2
3
4
5
6
7
func applyMutliplication(value: Int, multFunction: Int -> Int) -> Int {
return multFunction(value)
}

applyMutliplication(2, {value in
value * 3
})

闭包省略入参名

闭包入参可以不用参数名而是位置($0,$1,…)来访问

1
applyMutliplication(2, {$0 * 3})

甚至,如果闭包是函数的最后一个入参时,圆括号可以这样省略掉

1
applyMutliplication(2) {$0 * 3}

(翻译自 http://fuckingswiftblocksyntax.com/