実用的なGroovy: カレー化クロージャーによるファンクショナル・プログラミング

Groovyのクロージャーには他にも使い方が

Groovyではクロージャー(closures)を頻繁に使います。Groovyでのクロージャーの唯一の問題は、毎日のようにクロージャーを使うため、それが特別なものに思えなくなってしまうことです。今回はゲスト著者のKen BarclayとJohn Savageが、クロージャーの合成やVisitor設計パターンといった標準的な使い方を、カレーを少し加えて一味違った味付けにするヒントを解説します。curry() メソッドはHaskell Curryが発明したものですが、JSR準拠リリースの以前からGroovy言語の中にあったものです。

Andrew Glover, CTO, Vanward Technologies

Andrew GloverAndrew Gloverは合衆国ワシントン特別区にある、Vanward TechnologiesのCTO(最高技術責任者)です。Vanward Technologiesは自動化テスト・フレームワークの構築を専門としており、ソフトウェアのバグ発生数や統合時間やテスト時間の減少、また全体的なコード安定性改善に貢献しています。



Ken Barclay (k.barclay@napier.ac.uk), Lecturer, Napier University

ゲスト著者、Ken Barclayは、スコットランドのエジンバラにある、Napier UniversityのSchool of Computingの講師として、オブジェクト指向のソフトウェア・エンジニアリングや開発、プログラミングなどを専門としています。Object-oriented design with UML and Java (2003年Butterworth Heinemann刊)の共著者でもあります。



John Savage (j.savage@napier.ac.uk), Lecturer, Napier University

>ゲスト著者、John Savageは、スコットランドのエジンバラにある、Napier UniversityのSchool of Computingの講師として、オブジェクト指向のソフトウェア・エンジニアリングや開発、プログラミングなどを専門としています。Object-oriented design with UML and Java (2003年Butterworth Heinemann刊)の共著者でもあります。



2005年 8月 23日

1年ほど前に『実用的なGroovy』のシリーズを開始してから、何度かクロージャーを紹介してきました。alt.lang.jreシリーズの一部としてGroovyについて最初に書いた時(「Feeling Groovy」、2004年8月)にはGroovyのクロージャー構文を紹介し、前回は、クロージャー構文に関しても最近行われたJSR準拠のアップデートを紹介しました。これまで学んだことから、皆さんはGroovyのクロージャーが、メソッド・パラメーターとして参照され、パラメーター化され、渡されるコード・ブロックであること、またメソッド・コールからの戻り値として渡されることを学びました。さらにクロージャーは、他のクロージャーに対するパラメーターであったり、他のクロージャーからの戻り値であったりする場合もあります。クロージャーはClosureタイプのオブジェクトであるため、あるクラスのプロパティーである場合もあれば、コレクション(collection)のメンバーである場合もあります。

これらがどれも新味がないとは言いませんが、クロージャーに関して今回学ぶことは、これまで皆さんが学んだことよりも少し刺激的なことは請け合いです。ゲスト著者のJohn SavageとKen Barclayは、Groovyのクロージャーでのcurry() メソッドに関して興味深い実験を行ってきました。今回は、そうした彼らの成果を堪能できるというわけです。

このシリーズについて

どのようなツールであれ、開発作業の中に採り入れるためには、どういう場合に使うべきか、または使うべきではないかをよく知る必要があります。スクリプト言語は非常に強力なツールですが、その強力さは、適切なシナリオで適切な使い方をした場合にのみ発揮されます。『実用的なGroovy』シリーズはそうした点を念頭に置き、Groovyの実用的な使い方に焦点を絞って、どういう場合に、どのように使うのかを解説して行きます。

BarclayとSavageによるカリー化クロージャーの解説を読むと、合成やVisitor設計パターンなど、おなじみの操作に新たなスパイスが加わるでしょう。それと同時に、Groovyによるファンクショナル・プログラミングについてのヒントも得られるはずです。この記事は、Groovyの入った融合料理だと考えてください。

カリーをください!

スパイスの効いたカレーはインド料理店に行けば食べられますが、カリー化関数(curried function)は通常、MLやHaskell(参考文献)など、ファンクショナル・プログラミング言語に見られるものです。『カリー』という用語は、部分関数(partial function)の概念を開発したHaskell Curryという数学者からとったものです。『カリー化』というのは、多くの引数を取るファンクションの中に複数の引数を取り入れ、その残りの引数を使って結果を返す新しいファンクションを作ることを言います。素晴らしいことに、Groovyの現在のリリース(この原稿執筆時のバージョンはjsr-02)では、Closureオブジェクトに対してcurry() メソッドをサポートしています。これはつまり、我々Groovyの世界に住む住民としては、ファンクショナル・プログラミングの一面を利用できる、ということを意味するのです。

皆さんは恐らく今まで、curry() をいじったことはないでしょう。そこで、単純な、分かりやすい例から始めることにします。リスト1は、multiply(乗算)と呼ばれるクロージャーを示しています。このクロージャーは正式なパラメーター、xとyを持ち、この2つの値の積を返します。このコードは次に、multiplyクロージャーの実行方法として、(callの概念による)明示的なものと、(曖昧さは無いという前提の)暗示的な方法という2つを示しています。後者のスタイルから、ファンクション・コール表現が生まれます。

リスト1. 単純なクロージャー
def multiply = { x, y -> return x * y } // closure
def p = multiply.call(3, 4)             // explicit call
def q = multiply(4, 5)                  // implicit call
println "p: ${p}"                       // p is 12
println "q: ${q}"                       // q is 20

このクロージャーでも充分良いのですが。これをちょっとカリー化してみましょう。curry() メソッドを呼ぶ時には、実際のパラメーターの全てを与える必要はありません。『カリー化』コールによって、クロージャーを部分的に適用できるようになります。クロージャーの部分的適用は別のClosureオブジェクトであり、そこでは一部の値が固定されています。

リスト2は、multiplyクロージャーのカリー化を示しています。最初の例では、パラメーター xの値は、3に固定されています。tripleと呼ばれるクロージャーは、実質的にtriple = { y -> return 3 * y } という定義を持つことになります。

リスト2. カリー化クロージャー
def multiply = { x, y -> return x * y }  // closure
def triple = multiply.curry(3)           // triple = { y -> return 3 * y }
def quadruple = multiply.curry(4) 
// quadruple = { y -> return 4 * y }
def p = triple.call(4)                   // explicit call
def q = quadruple(5)                     // implicit call
println "p: ${p}"                        // p is 12
println "q: ${q}"                        // q is 20

ご覧の通り、パラメーター xはmultiplyの定義から削除され、すべて3という値で置き換えられています。

カリー化での計算の基礎

皆さんは基本的な数学の知識から知っていると思いますが、乗算の操作には交換法則が成り立ちます(つまりx * y = y * x)。しかし引き算では交換法則は成り立ちません。従って、引く値を扱う操作と、引かれる値を扱う操作、という2つの操作が必要です。リスト3は、このためにlSubtractとrSubtractというクロージャー(それぞれ左と右を表します)を定義するものですが、結果的にカリー化関数の面白い応用となっています。

リスト3. 左側オペランドと右側オペランド
def lSubtract = { x, y -> return x - y }
def rSubtract = { y, x -> return x - y }
def dec = rSubtract.curry(1)            
 // dec = { x -> return x - 1 }
def cent = lSubtract.curry(100)          
// cent = { y -> return 100 - y }
def p = dec.call(5)                      // explicit call
def q = cent(25)                         // implicit call
println "p: ${p}"                        // p is 4
println "q: ${q}"                        // q is 75

繰り返しと合成

このシリーズの以前の記事から、クロージャーが、ListコレクションやMapコレクションに適用されるイテレーター・メソッドによく使われることを思い出してください。例えばイテレーター・メソッド、collectは、コレクションの全要素にクロージャーを適用し、新しい値を持つ新しいコレクションを返します。リスト4は、ListとMapにcollectメソッドを適用するところを示しています。agesと呼ばれるListには、パラメーターとして単一クロージャー、{ element -> return element + 1 }を持つcollect() メソッドが送られます。ここで注意して欲しいのですが、Groovyでは、あるメソッドへのパラメーターの最後がクロージャーである場合は、それを実際のパラメーターのリストから除き、閉じ括弧の直後に置くことができるのです。実際の引数が何もない場合には、括弧を省略することができます。これは、accountsという名前のMapオブジェクトに呼ばれるcollect() メソッドで示されています。

リスト4. クロージャーとコレクション
def ages = [20, 30, 40]
def accounts = ['ABC123' : 200, 'DEF456' : 300, 'GHI789' : 400]
def ages1 = ages.collect({ element -> return element + 1 })
def accounts1 = accounts.collect 
  { entry -> entry.value += 10; return entry }
println "ages1: ${ages1}" 
// ages1: [21, 31, 41]
println "accounts1: ${accounts1}"
// accounts1: [ABC123=210, GHI789=410, DEF456=310]
def ages2 = ages.collect { element -> return dec(element) }
println "ages2: ${ages2}"
 // ages2: [19, 29, 39]

最後の例は、agesという名前のListから全ての要素を集め、それらに(リスト3の)decクロージャーを適用しています。

クロージャーの合成

クロージャーの最も重要な特徴の一つが、合成(composition)でしょう。合成では、他のクロージャーを組み合わせることを目的としたクロージャーを定義できます。合成を使うと、2つ、またはそれ以上の単純なクロージャーを組み合わせて、より複雑なクロージャーを作ることができます。

リスト5は、なかなか素晴らしいcompositionクロージャーを紹介しています。よく注意して読んでください。パラメーター、fとgは、単一パラメーターのクロージャーを表します。ここまでは、よろしいですね。さて、あるパラメーター値 xに対してクロージャー gを適用し、生成される結果にクロージャー fを適用します。なんと、最初の2つのクロージャー・パラメーターをカリー化することによって、実質的に、この2つの効果の組み合わせである新しいクロージャーが得られるのです。

リスト5は、クロージャー tripleとquadrupleを組み合わせ、クロージャー twelveTimesを生成しています。これを実際のパラメーターである3に適用すると、値36が戻ります。

リスト5. これがスーパー・クロージャー合成だ!
def multiply = { x, y -> return x * y }    
// closure
def triple = multiply.curry(3)            
// triple = { y -> return 3 * y }
def quadruple = multiply.curry(4) 
// quadruple = { y -> return 4 * y }
def composition = { f, g, x -> return f(g(x)) }
def twelveTimes = composition.curry(triple, quadruple)
def threeDozen = twelveTimes(3)
println "threeDozen: ${threeDozen}"		 
// threeDozen: 36

なかなかカッコ良いと思いませんか。


五つ星の計算

では、クロージャーの持つ、もっと興奮すべき面を調べてみましょう。まず、『計算パターン(pattern of computation)』を具体化するクロージャーの表現メカニズムから始めます(計算パターンは、ファンクショナル・プログラミングの概念です)。計算パターンの一例は、Listの要素すべてを何らかの方法で変換する場合などです。こうしたパターンは頻繁に起こるため、それらをstatic Closureとしてカプセル化する、Functorというクラスを作りました。リスト6は、その抜粋です。

リスト6. Functorが計算パターンをカプセル化する
package fp

abstract class Functor {
 //  arithmetic (binary, left commute and right commute)
 public static Closure bMultiply     = { x, y -> return x * y }
 public static Closure rMultiply     = { y, x -> return x * y }
 public static Closure lMultiply     = { x, y -> return x * y }
 
 // ...
 // composition
 public static Closure composition   = { f, g, x -> return f(g(x)) }
    
 // lists
 public static Closure map    = 
   { action, list -> return list.collect(action) }
    
 public static Closure apply  = { action, list -> list.each(action) }
    
 public static Closure forAll = { predicate, list ->
                                  for(element in list) {
                                    if(predicate(element) == false) {
                                        return false
                                    }
                                  }
                                  return true
                               }
    // ...
}

ここで、mapという名前のクロージャーがあります。これをMapインターフェースと混同しないでください。mapクロージャーは、クロージャーを表すパラメーター f`と、Listを表すパラメーター、listを持っています。このクロージャーは、listの全要素にfがマップされた、新しいListを返します。もちろん、GroovyにはList用に既にcollect() メソッドがあるので、この実装ではそれを使いました。

リスト7では、mapクロージャーを『カリー化』することによって一歩進め、与えられたリストの全要素を12倍するブロックとしています。

リスト7. 少しカリー化を追加し、12倍する
import fp.*

def twelveTimes = { x -> return 12 * x }
def twelveTimesAll = Functor.map.curry(twelveTimes)
def table = twelveTimesAll([1, 2, 3, 4])
println "table: ${table}"		
// table: [12, 24, 36, 48]

これこそ、五つ星の計算と呼ぶべきものでしょう!


カリー流のビジネス・ルール

こうしたクロージャーの例も悪くないかも知れませんが、もっとビジネス指向の読者には、次の例の方が良いでしょう。ある特定な、Bookというアイテムの実質的な価格を、店による値引きや付加価値税などの税金を考慮に入れた上で計算する問題を考えてみてください。このロジックをBookクラスの一部として含めてしまうと、できあがる解は、恐らく融通の利かないものになります。書店は値引きの幅を変えたり、店の在庫の一部にしか値引きを適用しなかったりするため、そうした解決方法では、厳格すぎてしまいます。

ところが、カリー化したクロージャーを使うと、ビジネス・ルールの変更が簡単にできるのです。個々のビジネス・ルールを表す一連の単純なクロージャーを使い、合成によってそれらを様々に組み合わせ、そして最後に、『計算パターン』を使ってコレクションにマップすればよいのです。

リスト8は、ある書店の例を示しています。クロージャー、rMultiplyは部分的適用であり、単項クロージャーとなるように、定数の第2オペランドを使ってバイナリー乗算を適用します。bookに関する2つのクロージャー、calcDiscountedPriceとcalcTaxは、rMultiplyクロージャーのインスタンスであり、乗算係数として固定値を持ちます。クロージャー、calcNetPriceは、値引きした値段を最初に計算した後、それに対する消費税を加え、実質的な価格を計算するアルゴリズムです。そして最後に、calcNetPriceが本の価格に対して適用されます。

リスト8. bookビジネス・オブジェクト
import fp.*

class Book {
    @Property name
    @Property author
    @Property price
    @Property category
}

def bk = new Book(name : 'Groovy', author : 
  'KenB', price : 25, category : 'CompSci')
// constants
def discountRate = 0.1
def taxRate = 0.17
//  book closures
def calcDiscountedPrice = Functor.rMultiply.curry(1 - discountRate)
def calcTax = Functor.rMultiply.curry(1 + taxRate)
def calcNetPrice = 
  Functor.composition.curry(calcTax, calcDiscountedPrice)
//  now calculate net prices
def netPrice = calcNetPrice(bk.price)
println "netPrice: ${netPrice}"		// netPrice: 26.325

よりGroovy的なビジター

機能パターンに対してカリー化クロージャーを適用する方法は見たので、同様の手法をオブジェクト指向での重要な設計パターンに適用するとどうなるかを調べてみましょう。オブジェクト指向のシステムでは、オブジェクトのコレクションをトラバースし、そのコレクションの各要素に対して何らかのアクションを行う、という場合が頻繁に発生します。別のシナリオとして、システムは同じコレクションをトラバースしますが、アクションは異なる、という場合もあります。通常、こうした要求には、Visitor設計パターン(参考文献)によって対応できます。Visitorインターフェースでは、コレクションの要素の処理に関して、アクション・プロトコルを導入しています。具象サブクラスは、それとは異なる要求振る舞いを定義しています。そして、コレクションをトラバースし、各要素に対してVisitorアクションを適用するメソッドが導入されています。

何のことか分からない人のために言うと、クロージャーを使えば、同じ効果が得られるのです。この方法の良いところは、クロージャーでは、ビジター・クラスの階層構造を作る必要がないことです。さらに、クロージャーの合成とマッピングを効果的に使えば、コレクションをトラバースしてアクションや効果を適用させることができるのです。

例えば、ある図書館の蔵書を表現するために、LibraryとBookというクラスの間での1対多の関係を考えてみてください。この関係は、ListあるいはMapを使って表現できますが、Mapでは、例えば鍵として本のカタログ番号を与えると、高速な検索ができるという利点があります。

リスト9は、Mapを使った、単純な1対多の関係を示しています。Libraryクラスでの2つの表示メソッドに注意してください。ビジターを導入すると、どちらもリファクタリングの対象候補となるのです。

リスト9. 図書館のアプリケーション
class Book {    
    @Property title
    @Property author
    @Property catalogNumber
    @Property onLoan = false

	String toString() {
        return "Title: ${title}; author: ${author}"
    }
}

class Library {
    @Property name
    @Property stock = [ : ]
    
	def addBook(title, author, catalogNumber) {
        def bk = new Book(title : title, author : 
          author, catalogNumber : catalogNumber)
        stock[catalogNumber] = bk
    }
    
    def lendBook(catalogNumber) {
        stock[catalogNumber].onLoan = true
    }
    
    def displayBooksOnLoan() {
        println "Library: ${name}"
        println "Books on loan"
        stock.each { entry ->
            if(entry.value.onLoan == true) println entry.value
        }
    }
    
    def displayBooksAvailableForLoan() {
        println "Library: ${name}"
        println "Books available for loan"
        stock.each { entry ->
            if(entry.value.onLoan == false) println entry.value
        }
    }    
}

def lib = new Library(name : 'Napier')
lib.addBook('Groovy', 'KenB', 
  'CS123')
lib.addBook('Java', 'JohnS', 'CS456')
lib.addBook('UML', 'Ken and John', 
  'CS789')
lib.lendBook('CS123')
lib.displayBooksOnLoan()	// Title: Groovy; author: KenB
lib.displayBooksAvailableForLoan()	// Title: UML; author: Ken and John
	// Title: Java; author: JohnS

リスト10には、Libraryクラスの中の、ビジターの使い方を真似た幾つかのクロージャーが含まれています。actionクロージャー(mapクロージャーと少し似ています)は、Listの要素すべてに対してactionクロージャーを適用します。クロージャー、displayLoanedBookは貸し出し中の本を表示し、クロージャー、displayAvailableBookは貸し出し中でない本を表示します。どちらも、ビジターとして、またその関連アクションとして動作します。applyクロージャーをdisplayLoanedBookでカリー化すると、本のコレクションを処理するクロージャー、displayLoanedBooksになります。リスト10では、貸し出し可能な本の一覧を表示するためにも、同じような方法が使われています。

リスト10. 図書館アプリケーションのビジター
import fp.*

class Book {
    @Property title
    @Property author

    @Property catalogNumber
    @Property onLoan = false

    String toString() {
        return "    Title: ${title}; author: ${author}"
    }
    
}

class Library {      
    @Property name
    @Property stock = [ : ]

    def addBook(title, author, catalogNumber) {
        def bk = new Book(title : title, author : 
          author, catalogNumber : catalogNumber)
        stock[catalogNumber] = bk
    }
    
    def lendBook(catalogNumber) {
        stock[catalogNumber].onLoan = true
    }
    
    def displayBooksOnLoan() {
        println "Library: ${name}"
        println "Books on loan"
        displayLoanedBooks(stock.values())
    }
    
    def displayBooksAvailableForLoan() {
        println "Library: ${name}"
        println "Books available for loan"
        displayAvailableBooks(stock.values())
    }
  
    
    private displayLoanedBook = { bk -> if(bk.onLoan == true) 
      println bk }
    private displayAvailableBook = { bk -> if(bk.onLoan == false) 
      println bk }
    
    private displayLoanedBooks = 
      Functor.apply.curry(displayLoanedBook)
    private displayAvailableBooks = 
      Functor.apply.curry(displayAvailableBook)
}

def lib = new Library(name : 'Napier')
lib.addBook('Groovy', 'KenB', 
  'CS123')
lib.addBook('Java', 'JohnS', 'CS456')
lib.addBook('UML', 'Ken and John', 
  'CS789')
lib.lendBook('CS123')
lib.displayBooksOnLoan()	// Title: Groovy; author: KenB
lib.displayBooksAvailableForLoan() // Title: UML; author: Ken and John
                             // Title: Java; author: JohnS

クロージャーによるテスト

この記事を終える前に、Groovyのクロージャーの使い方を、もう一つ見ておきましょう。多くのEmployee(従業員)を持つ、Company(会社)をモデル化したアプリケーションを考えてみてください。ここではCompanyとEmployeeの下に、1人のEmployee(チーム・リーダー)と多くのEmployee(チーム・メンバー)との関係という、さらなる1対多の再帰的関係ができます。図1は、こうした組織のクラス図を示しています。

図1. Companyというアプリケーション
図1. Companyというアプリケーション

このモデルのアーキテクチャー的な整合性を記述するために、クロージャーを使うことができるのです。例えば上記の例で、全従業員にそれぞれマネージャーが割り当てられていることを確認したいとしょう。単純なクロージャー、hasManagerを使ってdef hasManager = { employee -> return (employee.manager != null) } のようにすれば、1人の従業員に対する要求事項を表現できるのです。

先のリスト6の、FunctorクラスのforAllクロージャーを部分的に適用すると、アーキテクチャー的な要求、def everyEmployeeHasManager = Functor.forAll.curry(hasManager) を記述することができます。

リスト11は、システムのアーキテクチャー的整合性を調べるためにカリー化クロージャーを適用した例です。

リスト11. アーキテクチャー的整合性を調べるためのクロージャー
import fp.*
/**
 *  A company with any number of employees. 
 *  Each employee is responsible
 *  to a team leader who, in turn, manages a team of staff.
 */
import java.util.*

class Employee {
	@Property id
    @Property name
    @Property staff = [ : ]
    @Property manager = null

    String toString() {
        return "Employee: ${id} ${name}"
    }
    
    def addToTeam(employee) {
        staff[employee.id] = employee
        employee.manager = this
    }
	
}
class Company {
	@Property name
    @Property employees = [ : ]

    def hireEmployee(employee) {
        employees[employee.id] = employee
    }
    
    def displayStaff() {
        println "Company: ${name}"
        println "===================="
        employees.each { entry -> println "    
          ${entry.value}" }
    }
    
    
}
def co = new Company(name : 'Napier')
def emp1 = new Employee(id : 123, name : 'KenB')
def emp2 = new Employee(id : 456, name : 'JohnS')
def emp3 = new Employee(id : 789, name : 'JonK')
co.hireEmployee(emp1)
co.hireEmployee(emp2)
co.hireEmployee(emp3)
emp3.addToTeam(emp1)
emp3.addToTeam(emp2)
co.displayStaff()
//  Architectural closures
def hasManager = { employee -> return (employee.manager != null) }
def everyEmployeeHasManager = Functor.forAll.curry(hasManager)
def staff = new ArrayList(co.employees.values())
println "Every employee has a manager?: 
  ${everyEmployeeHasManager.call(staff)}"    // false

まとめ

今回は多くのクロージャーを見てきましたが、皆さんの新たな興味を呼び起こせたのではないかと思います。乗算の例で学んだように、カリー化クロージャーを利用すると、機能的な計算パターンの実装が驚くほど単純になります。こうしたパターンの扱いに慣れてくれば、書店の例でのビジネス・ルールのような一般的なエンタープライズ・シナリオにデプロイすることができます。機能パターンにクロージャーを適用するのは興奮に満ちたことですが、これに習熟すれば、オブジェクト指向の設計パターンに適用するのも、それほど難しいことではありません。またLibraryの例で見たように、カリー化クロージャーは、Visitorパターンでの必須要素を真似るためにも使用できます。さらにCompanyの例で見たように、ソフトウェア・テストでの整合性チェックにも使用することができるのです。

今回見てきた、こうした例は全て、エンタープライズ・システムでは一般的なものです。Groovyのクロージャーとcurryメソッドが、無数のプログラミング・シナリオに柔軟に適用できること、しかも機能パターンにもオブジェクト指向パターンにも適用できることを知ると、嬉しくなります。Haskell Curryも、きっと大喜びしていることでしょう。

参考文献

学ぶために

  • Groovyの成長が加速」(Andrew Glover著、developerWorks, 2005年7月)は、(クロージャーを含め)Groovyの構文における、JSRに準拠した変更の多くを取り上げています。
  • Functional programming in the Java language」(Abhijit Belapurkar著、developerWorks, 2004年7月)は、クロージャーや高階関数など、Java言語でのファンクショナル・プログラミング構成体の使い方に関する入門です。
  • Beginning Haskell」(David Mertz著、developerWorks, 2001年9月)は、ファンクショナル・プログラミングについて学びたい人のためのチュートリアルです。
  • 実用的なGroovyシリーズの全記事を読んでください。シリーズの進行につれて、それぞれの記事が相互に関係してきます。
  • Interview with Groovy project Manager Guillaume Laforgeでは、LaforgeがGroovyの将来や過去、なぜGroovyが重要なのかに関して、彼の考えを語っています。
  • ML for the Working Programmer(Lawrence C. Paulson著、1996年Cambridge University Press刊)は、このファンクショナル・プログラミング言語に関する入門として好適です。
  • Haskell: The Craft of Functional Programming (Simon Thompson著、1999年Addison Wesley刊)は、Haskellを学ぶための入門として好適です。
  • Design Patterns: Elements of Reusable Object-Oriented Software (Gammaらの共著、1995年Addison Wesley刊)では、この記事で議論した、オブジェクト指向設計パターンを学ぶことができます。
  • developerWorksのJava technologyゾーンでは、Javaプログラミングのあらゆる面を網羅した豊富な記事が用意されています。

製品や技術を入手するために

  • Groovy project pageからGroovyをダウンロードし、クロージャーやクロージャー合成に関して学んでください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=218882
ArticleTitle=実用的なGroovy: カレー化クロージャーによるファンクショナル・プログラミング
publish-date=08232005