Swift関数式プログラミング実践
13458 ワード
原文接続:a practical introduction to functional programming原文日付:2015/08/10
訳者:shanks校正:numbbbbb定稿:小鍋
SwiftはiOSプログラミングの世界に新しいモデルを導入した:関数モデル.多くのiOS開発者は、Objective-Cや他のオブジェクト向けプログラミング言語に慣れており、関数式の符号化や思考が少し頭を焼くようになっています.そこから勉強を始めるべきですか.私はいくつかの非常に理解しやすい例を見つけました-Mary Rose Cookのblogの中で1篇のとても良い文章を見つけました:A practical introduction to functional programming、この文章はとても良くて、私の疑問を解くのに十分で、しかもこの文章は多くの例のコードを含んで、私达はこの基礎の上で関数式の補充を加えることができます.
私たちのこの文章はCookの例を見直し、Swiftで実現します.だから、この文章を読む前に、まず彼女の文章を読んでください.この文章は多くの例を作っただけでなく、初心者にとって、関数式プログラミングとは何かをはっきり説明しています.私はこれらの概念を繰り返しません.
プログラマーたちが関数式のプログラミングについて話しているとき、彼らは多くのくわえ天の「関数式」の特性に言及します.のしかし、これらの点を無視してください.関数コードは、副作用がないことを説明しています.現在の関数は、関数以外のデータに依存せず、現在の関数も関数以外のデータを変更しません.他のすべての関数プロパティは、このプロパティから拡張されます.この特性を関数式プログラミングを学ぶ指導思想としてください.
-Mary Rose Cook関数式プログラミングの経験を学ぶ方法について
この2つの関数の違いは、変数a−関数
関数プログラミングを深く研究するプログラマーは、
例が示すように、
ここでは,熟知した方法を用いてランダムなプログラミング言語配列を得る方法,および我々の関数式の補完実装を見ることができる.
Reduce関数は、1つの関数と1つの集合を受信します.要素を結合して作成した値を返します.
上記の例では、パラメータ名の略語
0から、加算接続
Swift 1.2では、
関数バージョンのコマンドコードは宣言されます.このようなコードは、どのようにするかではなく、何をするかを説明しています.のコードをいくつかの関数にパッケージ化すると、コードの宣言の性質が向上します.
Objective-C開発者はコマンドプログラミングに慣れています.これはプログラミングのモデルで、一連の文がステータスを変更するために使用されています.関数プログラミングは宣言プログラミングの一種であり、関数プログラミングの特徴は関数を使用して何をするかを記述することである.
Cookの例を見てみましょう.次のコードは3つの自動車の競争をシミュレートしています.
経験のあるObjective-C開発者は、上のコードを見て、上のコードをより小さなセグメントに分解すべきだとすぐに気づきます.
コードはより簡潔になりましたが、依然として関数式ではありません.各関数はCookが教えてくれた関数式に従って実現されていません.「[関数は現在の関数以外のデータに依存することができず、現在の関数以外のデータを変更することはできません」.
以下は関数実装バージョンです.
わあ、コードがたくさんありますね.次の例の各関数をよく検討することをお勧めします.外部データに依存したり、内部データを変更したりしない関数がほしいことに気づきます.
例のもう1つの清潔さを保つ詳細は、
前の章では、いくつかのコマンド式のループが補助関数を呼び出すループに書き換えられています.この章では、別のコマンド式のループは、パイプという技術を使用して書き換えられます.
まず、データのセットを変換する典型的な例を示します.この例では、各要素が
以下は、データ変換を同様に実現し、すべてのコードを
私たちはどのように実現すべきですか?
上記のすべての関数を使用すると、それらの役割を独立して考えることができます.入力された
ここを見たら、別の合併変換の方法を見てみましょう.Cookの文章にはこの方法が紹介されていません.
この方法のインスピレーションは私が一番好きなSwiftに関する図書:Functional Programming in Swiftから来て、作者はobjcから来ました.ioのChris Eidhof、Florian Kugler、Wouter Swierstra.この本の章では、関数の構築方法について説明しています.この概念を前のコードに使用することができます.
これは以前のコードと似ているように見えますが、関数構築の概念を使用して私たちの変換を構築し、Cookの
この文章はCookのすばらしい総括で終わることができます.
関数コードは他のスタイルのコードとよく共存することができます......リストのループを
Swiftは純粋な関数言語ではありません.あなたの関数コードは非関数コードとよく共存することができます.
この文章の重点は、既存のコードを関数スタイルに変換する方法を教え、関数プログラミングの威力を見せることです.
本文のすべての例のコードはGists(原文の例のGistsリンクをクリックしてください)に置かれ、GitHubにも置かれています.https://github.com/hkellaway/swift-functional-intro
コードハッピー!
訳者:shanks校正:numbbbbb定稿:小鍋
紹介する
SwiftはiOSプログラミングの世界に新しいモデルを導入した:関数モデル.多くのiOS開発者は、Objective-Cや他のオブジェクト向けプログラミング言語に慣れており、関数式の符号化や思考が少し頭を焼くようになっています.そこから勉強を始めるべきですか.私はいくつかの非常に理解しやすい例を見つけました-Mary Rose Cookのblogの中で1篇のとても良い文章を見つけました:A practical introduction to functional programming、この文章はとても良くて、私の疑問を解くのに十分で、しかもこの文章は多くの例のコードを含んで、私达はこの基礎の上で関数式の補充を加えることができます.
私たちのこの文章はCookの例を見直し、Swiftで実現します.だから、この文章を読む前に、まず彼女の文章を読んでください.この文章は多くの例を作っただけでなく、初心者にとって、関数式プログラミングとは何かをはっきり説明しています.私はこれらの概念を繰り返しません.
プログラマーたちが関数式のプログラミングについて話しているとき、彼らは多くのくわえ天の「関数式」の特性に言及します.のしかし、これらの点を無視してください.関数コードは、副作用がないことを説明しています.現在の関数は、関数以外のデータに依存せず、現在の関数も関数以外のデータを変更しません.他のすべての関数プロパティは、このプロパティから拡張されます.この特性を関数式プログラミングを学ぶ指導思想としてください.
-Mary Rose Cook関数式プログラミングの経験を学ぶ方法について
ケース#1-Increment
/* : 。 */
/// ///
var a = 0
func incrementUnfunctional() -> () {
a += 1
}
incrementUnfunctional()
print(a) // a = 1
/// ///
a = 0
func incrementFunctional(num: Int) -> Int {
return num + 1
}
a = incrementFunctional(a)
print(a) // a = 1
この2つの関数の違いは、変数a−関数
incrementUnfunctional
をどのように増加させるかによってグローバル変数が変更され、関数incrementFunctional
は通常の関数であり、1つの数値を取得し、増加した数値を返す.これがCookが言及した「副作用なし」:関数incrementFunctional
が自身以外の変数に影響を及ぼさない状態である.cookのレッスン1:リストでループを使用しないでmapとreduceを使用
関数プログラミングを深く研究するプログラマーは、
map
、reduce
およびfilter
関数にすぐに接触します.これらの関数は、コレクションタイプを処理するのに非常に強力です.次に、map
およびreduce
関数を見てみましょう.例2-Map 1
Map
関数は、関数と集合を受信します.Map
は、新しい空の集合を生成し、入力された関数を使用して集合の各要素を処理し、戻り値を新しい集合に挿入し、最後にこの新しい集合を返します./* , map reduce。 */
// Map #1
let languages = ["Objective-C", "Java", "Smalltalk"]
let languageLengths = languages.map { language in count(language) }
print(languageLengths) // [11, 4, 9]
let squares = [0, 1, 2, 3, 4].map { x in x * x }
print(squares) // [0, 1, 4, 9, 16]
例が示すように、
map
は確かに新しい集合を返します.この例では配列(array)です.元の集合の各要素を匿名関数で処理し、元の集合は変わらないままです.例3-Map 2
/* , map reduce。 */
// Map #2
var languages = ["Objective-C", "Java", "Smalltalk"]
let newLanguages = ["Swift", "Haskell", "Erlang"]
/// ///
for index in 0.. String {
let randomIndex = randomPositiveNumberUpTo(array.count)
return array[randomIndex]
}
func randomPositiveNumberUpTo(upperBound: Int) -> Int {
return Int(arc4random_uniform(UInt32(uppderBound)))
}
ここでは,熟知した方法を用いてランダムなプログラミング言語配列を得る方法,および我々の関数式の補完実装を見ることができる.
例#4-Reduce 1
Reduce関数は、1つの関数と1つの集合を受信します.要素を結合して作成した値を返します.
/* , map reduce。 */
// Reduce #1
let sum = [0, 1, 2, 3, 4].reduce(0, combine: { $0 + $1 })
print(sum) // 10
Reduce
理解するのはmap
より難しいです.Reduce
は、初期値(上記の例では、初期値は0)から値の蓄積を開始し、各集合要素でcombine
閉パケットを呼び出し、最後の結果を返す.上記の例では、パラメータ名の略語
$0
と$1
を使用していますが、これはよくわかりません.以下は別の表現で、機能は同じです./* , map reduce。 */
// Reduce #1 -
let numbers = [0, 1, 2, 3, 4]
let startingWith = 0
let sum = numbers.reduce(startingWith) {
(runningSum, currentNumber) in
runningSum + currentNumber
}
print(sum) // 10
0から、加算接続
runningSum
と数値セットの現在の値を使用して、最終的に合計を完了します.例#5-Reduce 2
Reduce
は、数値セットだけでなく、文字列セットをどのように処理するかを見てみましょう.次の関数は、「hello」という単語を含むフレーズがいくつあるかを示します./* , map reduce。 */
// Reduce #2
let greetings = ["Hello, World", "Hello, Swift", "Later, Objective-C"]
/// ///
var helloCount = 0
for greeting in greetings {
if(string(greeting, contains:"hello")) {
helloCount += 1
}
}
print(helloCount) // 2
/// ///
let helloCountFunctional = greetings.reduce(0, combine: { $0 + ((string($1, contains:"hello")) ? 1 : 0) })
print(helloCountFunctional) // 2
//
func string(str: String, #contains: String) -> Bool {
return str.lowercaseString.rangeOfString(contains.lowercaseString) != nil
}
Swiftノート:MapとReduce
Swift 1.2では、
map
およびreduce
のような関数がSwiftライブラリでグローバル関数であるため、map([0, 1, 2, 3, 4], { x in x * x })
を使用する必要があります.Swift 2は、上記の例で見たように、map
をコレクション上で直接呼び出すことができるより直感的な構文を提供します.この2つの方法の機能は同じです!Cookのレッスン2-宣言式(Imperative)を使用してプログラミングし、コマンド式(Declarative)を使用しない
関数バージョンのコマンドコードは宣言されます.このようなコードは、どのようにするかではなく、何をするかを説明しています.のコードをいくつかの関数にパッケージ化すると、コードの宣言の性質が向上します.
Objective-C開発者はコマンドプログラミングに慣れています.これはプログラミングのモデルで、一連の文がステータスを変更するために使用されています.関数プログラミングは宣言プログラミングの一種であり、関数プログラミングの特徴は関数を使用して何をするかを記述することである.
例#6-Imperative 1
Cookの例を見てみましょう.次のコードは3つの自動車の競争をシミュレートしています.
/*** , ***/
// Imperative vs. Declarative - #1
/// Imperative - ///
var time = 5
var carPositions = [1, 1, 1]
while(time > 0) {
time -= 1
print("
")
for index in 0.. 3) {
carPositions[index] += 1
}
for _ in 0..
例#7-Imperative 2
経験のあるObjective-C開発者は、上のコードを見て、上のコードをより小さなセグメントに分解すべきだとすぐに気づきます.
/*** , ***/
// Imperative vs. Declarative - #2
/// Imperative - ///
var time = 5
var carPositions = [1, 1, 1]
while(time > 0) {
runStepOfRace()
draw()
}
// Helpers
func runStepOfRace() -> () {
time -= 1
moveCars()
}
func draw() {
print("
")
for carPosition in carPositions {
drawCar(carPosition)
}
}
func moveCars() -> () {
for index in 0.. 3) {
carPositions[index] += 1
}
}
}
func drawCar(carPosition: Int) -> () {
for _ in 0..
コードはより簡潔になりましたが、依然として関数式ではありません.各関数はCookが教えてくれた関数式に従って実現されていません.「[関数は現在の関数以外のデータに依存することができず、現在の関数以外のデータを変更することはできません」.
例#8-Declarative
以下は関数実装バージョンです.
/*** , ***/
// Imperative vs. Declarative - #3
/// ///
typealias Time = Int
typealias Positions = [Int]
typealias State = (time: Time, positions: Positions)
let state: State = (time: 5, positions: [1, 1, 1])
race(state)
//
func race(state: State) -> () {
draw(state)
if(state.time > 1) {
print("
")
race(runStepOfRace(state))
}
}
func draw(state: State) -> () {
let outputs = state.positions.map { position in outputCar(position) }
print(join("
", outputs))
}
func runStepOfRace(state: State) -> State {
let newTime = state.time - 1
let newPositions = moveCars(state.positions)
return (newTime, newPositions)
}
func outputCar(carPosition: Int) -> String {
let output = (0.. [Int] {
return positions.map { position in (randomPositiveNumberUpTo(10) >
3) ? position + 1 : position }
}
わあ、コードがたくさんありますね.次の例の各関数をよく検討することをお勧めします.外部データに依存したり、内部データを変更したりしない関数がほしいことに気づきます.
例のもう1つの清潔さを保つ詳細は、
typealias
キーワードを介してbands
キーワードを使用することで、コードを書くときにより自然になります.個人的に関数式プログラミングが好きな理由の一つは、コードで使用されるタイプと、関数でこれらのタイプをどのように操作するかをよく考えさせることです.cookのレッスン3-パイプを使用します。
前の章では、いくつかのコマンド式のループが補助関数を呼び出すループに書き換えられています.この章では、別のコマンド式のループは、パイプという技術を使用して書き換えられます.
例#9-パイプを使用しない
まず、データのセットを変換する典型的な例を示します.この例では、各要素が
name
およびcountry
を含むbands
の配列がある.このcountry
集合を2回変換したい:1、Canada
をname
2に設定し、inout
を大文字に変更します.以下は私たちが初めて実現したものです./*** ***/
/// ///
var bands: [ [String : String] ] = [
["name" : "sunset rubdown", "country" : "UK"],
["name" : "women", "country" : "Germany"],
["name" : "a silver mt. zion", "country" : "Spain"]
]
func formatBands(inout bands: [ [String : String] ]) -> () {
var newBands: [ [String : String] ] = []
for band in bands {
var newBand: [String : String] = band
newBand["country"] = "Canada"
newBand["name"] = newBand["name"]!.capitalizedString
newBands.append(newBand)
}
bands = newBands
}
formatBands(&bands)
print(bands) // [[country: Canada, name: Sunset Rubdown], [country: Canada, name: Women], [country: Canada, name: A Silver Mt. Zion]]
formatBands
キーワードを使用すると、関数プログラミングを使用していないことがわかります.以下は、データ変換を同様に実現し、すべてのコードを
print(formattedBands(bands, [setCanadaAsCountry, capitalizeName]))
関数に詰め込むことなく、より表現力と柔軟性を有する別の実装方法である.canada
私たちはどのように実現すべきですか?
例#10-関数パイプ
/*** ***/
/// Functional - Example #1 ///
let bands: [ [String : String] ] = [
["name" : "sunset rubdown", "country" : "UK"],
["name" : "women", "country" : "Germany"],
["name" : "a silver mt. zion", "country" : "Spain"]
]
typealias BandProperty = String
typealias Band = [String : BandProperty]
typealias BandTransform = Band -> Band
typealias BandPropertyTransform = BandProperty -> BandProperty
let canada: BandPropertyTransform = { _ in return "Canada" }
let capitalize: BandPropertyTransform = { return $0.capitalizedString }
let setCanadaAsCountry: BandTransform = call(function: canada, onValueForKey: "country")
let capitalizeName: BandTransform = call(function: capitalize, onValueForKey: "name")
func formattedBands(bands: [Band], functions: [BandTransform]) -> [Band] {
return bands.map {
band in
functions.reduce(band) {
(currentBand, function) in
function(currentBand)
}
}
}
print(formattedBands(bands, [setCanadaAsCountry, capitalizeName])) // [[country: Canada, name: Sunset Rubdown], [country: Canada, name: Women], [country: Canada, name: A Silver Mt. Zion]]
//
func call(#function: BandPropertyTransform, onValueForKey key: String) -> BandTransform {
return {
band in
var newBand = band
newBand[key] = function(band[key]!)
return newBand
}
}
capitalize
およびBandProperty
関数に注意してください.彼らはBandProperty
(文字列)を受け入れてsetCountryAsCanada
(文字列)を返すだけです.capitalizeName
およびBandPropertyTransform
関数の表示に注意してください.彼らは単純にcanada
関数(例えばcapitalize
およびBand
)を受信し、formattedBands
(辞書)のキー値(ここではそれぞれ「country」および「name」)に適用する.上記のすべての関数を使用すると、それらの役割を独立して考えることができます.入力された
BandTransform
配列を処理するために、bands
の関数が最後に呼び出される.私たちは任意の数の変換を書くことができて、それからそれらをformattedBands
に伝えて、同時に他の関数を維持します--これはとても強い東です!課外コンテンツ-関数の組合せ
ここを見たら、別の合併変換の方法を見てみましょう.Cookの文章にはこの方法が紹介されていません.
この方法のインスピレーションは私が一番好きなSwiftに関する図書:Functional Programming in Swiftから来て、作者はobjcから来ました.ioのChris Eidhof、Florian Kugler、Wouter Swierstra.この本の章では、関数の構築方法について説明しています.この概念を前のコードに使用することができます.
例#11-関数の組合せ
/*** ***/
/// - #2 ///
let canada: BandPropertyTransform = { _ in return "Canada" }
let capitalize: BandPropertyTransform = { return $0.capitalizedString }
let setCanadaAsCountry: BandTransform = call(function: canada, onValueForKey: "country")
let capitalizeName: BandTransform = call(function: capitalize, onValueForKey: "name")
let myBandTransform = composeBandTransforms(setCanadaAsCountry, capitalizeName)
let formattedBands = bands.map { band in myBandTransform(band) }
print(formattedBands) // [[country: Canada, name: Sunset Rubdown], [country: Canada, name: Women], [country: Canada, name: A Silver Mt. Zion]]
//
func call(#function: BandPropertyTransform, onValueForKey key: String) -> BandTransform {
return {
band in
var newBand = band
newBand[key] = function(band[key]!)
return newBand
}
}
func composeBandTransforms(transform1: BandTransform, transform2: BandTransform) -> BandTransform {
return {
band in
transform2(transform1(band))
}
}
これは以前のコードと似ているように見えますが、関数構築の概念を使用して私たちの変換を構築し、Cookの
map
とreduce
スキームを置き換えます.結論
この文章はCookのすばらしい総括で終わることができます.
関数コードは他のスタイルのコードとよく共存することができます......リストのループを
map
とreduce
に変えます.この点は車両レースの例を参考にしてください.コードを関数に分割し、これらの関数をより関数的にします.一つの過程の循環を再帰に変える.この点はbands
の例を参照してください.一連の操作をパイプに変える.Swiftは純粋な関数言語ではありません.あなたの関数コードは非関数コードとよく共存することができます.
この文章の重点は、既存のコードを関数スタイルに変換する方法を教え、関数プログラミングの威力を見せることです.
本文のすべての例のコードはGists(原文の例のGistsリンクをクリックしてください)に置かれ、GitHubにも置かれています.https://github.com/hkellaway/swift-functional-intro
コードハッピー!