Excerpt
| hidden | true |
|---|
| hidden | true |
|---|
Functional Basics
関数型の基礎
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
def fac(n) { n == 0 ? 1 : n * fac(n - 1) }
assert 24 == fac(4)
|
Excerpt
| hidden | true |
|---|
return statements (the last evaluated expression is the default return value). Groovyでは、 return 文を省略できる(最後に評価された式がデフォルトの戻り値となります)という微量のシンタクスシュガーがJavaに加えられています。Excerpt
| hidden | true |
|---|
| Code Block |
|---|
def fac2 = { n -> n == 0 ? 1 : n * call(n - 1) }
assert 24 == fac2(4)
|
Excerpt
| hidden | true |
|---|
fac2.call(4) is also supported). The closure version has the advantage that we can pass the fac2 Closure around (e.g. as a parameter) or place it within a data structure. 最後の行に見えるように、このクロージャ版はメソッドと同様の方法で呼び出されています(ですが、 fac2.call(4) という長い書き方もサポートされています)。クロージャ版は fac2 をClosureパラメータとして渡したり、データ構造に埋め込んだりできるといった利点があります。Excerpt
| hidden | true |
|---|
| Code Block |
|---|
def fac3 = this.&fac assert 24 == fac3(4) |
Excerpt
| hidden | true |
|---|
fac3 variable will also be a closure. fac3 変数もまたクロージャとなります。Excerpt
| hidden | true |
|---|
| Code Block |
|---|
def sort(list) {
if (list.isEmpty()) return list
anItem = list[0]
def smallerItems = list.findAll{it < anItem}
def equalItems = list.findAll{it == anItem}
def largerItems = list.findAll{it > anItem}
sort(smallerItems) + equalItems + sort(largerItems)
}
assert [1, 3, 4, 5] == sort([1, 4, 5, 3])
assert [1, 1, 3, 4, 4, 5, 8] == sort([4, 1, 4, 1, 8, 5, 3])
assert ['a', 'b', 'c'] == sort(['c', 'b', 'a'])
|
...
| hidden | true |
|---|
Curry functions
カリー関数
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
def joinTwoWordsWithSymbol = { symbol, first, second -> first + symbol + second }
assert joinTwoWordsWithSymbol('#', 'Hello', 'World') == 'Hello#World'
def concatWords = joinTwoWordsWithSymbol.curry(' ')
assert concatWords('Hello', 'World') == 'Hello World'
def prependHello = concatWords.curry('Hello')
// def prependHello = joinTwoWordsWithSymbol.curry(' ', 'Hello')
assert prependHello('World') == 'Hello World'
|
Excerpt
| hidden | true |
|---|
| hidden | true |
|---|
Lazy evaluation
遅延評価
Excerpt
| hidden | true |
|---|
| hidden | true |
|---|
...
| hidden | true |
|---|
XmlSlurperallows arbitrary GPath expressions to be crafted. As you create the expressions, you might think that XML parsing is going on behind the covers pulling XML nodes into and out of lists to match what your expressions are asking for. This is not the case. Instead a lazy representation of your GPath is stored away. When you need to evaluate the final result of a GPath expression, it calculates just what it needs to determine the expressions resulting value. [See chapter 12 of GINA for more information about XmlSlurper.]
...
- Groovy's
DataSetfeature does the some thing for data stored in relational databases. As you build up your dataset queries, no connections or operations to the database are happening under the covers. At the time when you need the result, an optimised query minimising SQL traffic is invoked to return the required result. [See section 10.2 of GINA for more information about DataSets.]
...
| hidden | true |
|---|
Infinite structures
無限の構造
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
// general purpose lazy list class
class LazyList {
def car
private Closure cdr
LazyList(def car, Closure cdr) { this.car=car; this.cdr=cdr }
def LazyList getCdr() { cdr ? cdr.call() : null }
def List take(n) {
def r = []; def l = this
n.times { r.add(l.car); l = l.cdr }
r
}
def LazyList filter(Closure pred) {
if (pred(car)) return pred.owner.cons(car, { getCdr().filter(pred) })
else return getCdr().filter(pred)
}
}
// general purpose lazy list function
def cons(val, Closure c) { new LazyList(val, c) }
// now define and use infinite streams
def integers(n) { cons(n, { integers(n+1) }) }
def naturalnumbers = integers(1)
assert '1 2 3 4 5 6 7 8 9 10' == naturalnumbers.take(10).join(' ')
def evennumbers = naturalnumbers.filter{ it % 2 == 0 }
assert '2 4 6 8 10 12 14 16 18 20' == evennumbers.take(10).join(' ')
|
Excerpt
| hidden | true |
|---|
cons, car and cdr, you may find the refactored version below (creds to Alexander Kriegisch) somewhat more readable: もしあなたが、cons, car, cdr のような伝統的な関数型プログラミングの用語に慣れ親しんでいないなら、リファクタリングして少し読みやすくしたバージョン(Alexander Kriegisch)はいかがでしょうか。| Code Block |
|---|
class LazyList {
def head
private Closure tail
LazyList(def head, Closure tail) { this.head=head; this.tail=tail }
def LazyList getTail() { tail ? tail.call() : null }
def List getFirst(n) {
def result = []; def current = this
n.times { result.add(current.head); current = current.tail }
result
}
def LazyList filter(Closure matchExpr) {
if (matchExpr(head))
return matchExpr.owner.prepend(head, { getTail().filter(matchExpr) })
else
return getTail().filter(matchExpr)
}
}
// general purpose lazy list function
def prepend(val, Closure c) { new LazyList(val, c) }
// now define and use infinite streams
def integers(n) { prepend(n, { integers(n+1) }) }
def naturalnumbers = integers(1)
assert '1 2 3 4 5 6 7 8 9 10' == naturalnumbers.getFirst(10).join(' ')
def evennumbers = naturalnumbers.filter{ it % 2 == 0 }
assert '2 4 6 8 10 12 14 16 18 20' == evennumbers.getFirst(10).join(' ')
|
...
| hidden | true |
|---|
Using Functional Java
Functional Javaを使用する
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
import fj.data.Stream
// some metaprogramming to make fj mesh well with Groovy
Stream.metaClass.filter = { Closure c -> delegate.filter(c as fj.F) }
Stream.metaClass.getAt = { n -> delegate.index(n) }
Stream.metaClass.getAt = { Range r -> r.collect{ delegate.index(it) } }
Stream.metaClass.asList = { delegate.toCollection().asList() }
def evens = Stream.range(0).filter{ it % 2 == 0 }
assert evens.take(5).asList() == [0, 2, 4, 6, 8]
assert (8..12).collect{ evens[it] } == [16, 18, 20, 22, 24]
assert evens[3..6] == [6, 8, 10, 12]
|
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
import fj.data.Stream
import static fj.data.Stream.cons
// some metaprogramming to make Closures mesh well with fj functions and products
Stream.metaClass.filterTail = { Closure c -> delegate.tail()._1().filter(c as fj.F) }
Stream.metaClass.static.cons = { h, Closure c -> delegate.cons(h, ['_1':c] as fj.P1) }
def sieve(nums) {
cons nums.head(), { sieve nums.filterTail{ n -> n % nums.head() != 0 } }
}
println sieve(Stream.range(2)).index(100) // => 547
|
Excerpt
| hidden | true |
|---|
functionaljava.jar to your classpath. You will need a version greater than version 2.8 of Functional Java. functionaljava.jarをクラスパスに追加するのを忘れないようにして下さい。バージョン2.8以上のFunctional Javaが必要です。Excerpt
| hidden | true |
|---|
| Code Block |
|---|
@Grab('org.functionaljava:functionaljava:3.0')
import static fj.data.Array.array
import static fj.data.List.list
import static fj.Show.*
import fj.F
arrayShow(intShow).println(array(1, 2, 3).map({ it + 42 } as F)) // => {43,44,45}
assert array(1, 2, 3).map({ it + 42 } as F).toCollection().toList() == [43, 44, 45]
assert [1, 2, 3].collect{ it + 42 } == [43, 44, 45] // native Groovy for comparison
listShow(intShow).println(list(1, 2, 3).bind({ it % 2 ? list(it * 2) : list() } as F)) // => <2,6>
assert list(1, 2, 3).bind({ it % 2 ? list(it * 2) : list() } as F).toCollection().toList() == [2, 6]
assert [1, 2, 3].collect{ it % 2 ? [it * 2] : [] }.sum() == [2, 6] // native Groovy for comparison
|
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
import static fj.data.Java.ArrayList_List as asList
fj.data.List.metaClass.toList = { delegate.toCollection().toList() }
List.metaClass.bind = { c -> asList().f(delegate).bind({ asList().f(c(it)) } as F) }
List.metaClass.map = { c -> asList().f(delegate).map(c as F) }
|
Excerpt
| hidden | true |
|---|
| Code Block |
|---|
assert [1, 2, 3].bind{ it % 2 ? [it * 2] : [] }.toList() == [2, 6]
assert [1, 2, 3].map{ it + 42 }.toList() == [43, 44, 45]
|
...
| hidden | true |
|---|
More Information
詳細情報
Excerpt
| hidden | true |
|---|
- Practically Groovy: Functional programming with curried closures
- Infinite Streams in Groovy
- Functional Programming Languages
- Why Functional Programming Matters
- Functional programming in the Java language
- Post on functional programming in Java - maybe a tad verbose
- FunctionalJ - A library for Functional Programming in Java
- Weak versus strong languages, who wins the fight?
- Programming Languages:Application and Interpretation
- Beyond Groovy 1.0: Groovy goes Lisp
- Japanese Concurrency with Groovy