Natasha Murashevによる非常に有用な記事のロシア語翻訳
The Swift Functionsの多くの顔 。
Objective-Cの構文は他のプログラミング言語と比べてやや奇妙ですが、
メソッドの構文は単純で単純です。 ここに過去への短い余談があります:
+ (void)mySimpleMethod {
対照的に、
Swift構文はほとんどの場合、他のプログラミング言語と同じように見えますが、
Objective-Cよりも複雑でわかりにくい場合があります。
続行する前に、この記事全体でこれらの用語の両方を使用するため、
Swiftの 「メソッド」と
「関数」の違いを明確にします。
AppleのSwiftプログラミング言語で指定されている「メソッド」の定義は次のとおりです。
メソッドは、特定の「タイプ」に関連付けられている関数です。 クラス、構造、および列挙は、特定のタイプのインスタンスを操作するための特定の作業と機能をカプセル化するインスタンスメソッドを定義できます。 クラス、構造、および列挙は、「タイプ」に関連付けられた「タイプ」のメソッドを定義することもできます。 「タイプ」メソッドは、Objective-Cの「クラス」メソッドに似ています。
関数は自律
的ですが 、
メソッドは
class, struct
または
enum
組み込まれた関数です。
Swift関数の構造
シンプルな「Hello、World!」
Swift関数から始めましょう。
func mySimpleFunction() { println("hello, world!") }
Objective-C以外の言語でプログラミングしたことがあれば、この機能はおなじみのように思えます。
キーワード
func
は、それが関数であることを意味します。
この関数の名前は
mySimpleFunction
です。
パラメータはこの関数に渡されません—括弧()内は空のためです。
値は返されません。
実行可能な関数コードは、中括弧{}の間にあります。
それでは、より複雑な関数に移りましょう。
func myFunctionName(param1: String, param2: Int) -> String { return "hello, world!" }
この関数は
param1
String
型の
param1
という名前のパラメーターと
param2
Int
型の
param2
という名前のパラメーターを取り、
String
型の値を返します。
すべての関数を呼び出す
スウィフト関数と呼ば際
スウィフトと
Objective-Cとの間に有意な違いの1つは
方法の設定作業です。 私が好むように、
Objective-Cのおしゃべりが好きなら、
Swift関数を呼び出すときにパラメーター名がデフォルトで含まれないことに注意してください:
func hello(name: String) { println("hello \(name)") } hello("Mr. Roboto")
関数にさらにいくつかのパラメーターを追加するまで、それほど悪くはありません。
func hello(name: String, age: Int, location: String) { println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?") } hello("Mr. Roboto", 5, "San Francisco")
hello("Mr. Roboto", 5, "San Francisco")
読むには、どのパラメーターが何を意味するかを知る必要があります。
Swiftには、この混乱を明確にするための外部パラメーター名の概念があります。
func hello(name name: String) { println("hello \(name)") } hello(name: "Robot")
代わりに、略語のパラメーター名の前に#を追加します。
func hello(#name: String) { println("hello \(name)") } hello(name: "Robot")
そして、もちろん、
関数のパラメーターが
機能するルールは、
メソッドのルールとは少し異なり
ます ...
メソッド呼び出し
関数が
class
(または
struct
、または
enum
)に組み込まれている場合、メソッドの
最初のパラメーターの名前は
externalとして含まれませんが、メソッドが呼び出されると、後続のパラメーター名はすべて
外部として含まれます。
class MyFunClass { func hello(name: String, age: Int, location: String) { println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?") } } let myFunClass = MyFunClass() myFunClass.hello("Mr. Roboto", age: 5, location: "San Francisco")
したがって、
Objective-Cのように、メソッドの名前に最初のパラメーターの名前を含めることをお
勧めします。
class MyFunClass { func helloWithName(name: String, age: Int, location: String) { println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?") } } let myFunClass = MyFunClass() myFunClass.helloWithName("Mr. Roboto", age: 5, location: "San Francisco")
“hello”
関数を呼び出す代わりに、
helloWithName
に名前を変更して、メソッドの最初のパラメーターの名前をより明確にしました。
何らかの理由で関数内の外部パラメーター名をスキップする場合(非常に重要な理由がある場合にのみこれを行うことをお勧めします)、外部パラメーター名に_記号を使用します。
class MyFunClass { func helloWithName(name: String, _ age: Int, _ location: String) { println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?") } } let myFunClass = MyFunClass() myFunClass.helloWithName("Mr. Roboto", 5, "San Francisco")
インスタンスメソッドはカリー化された関数です
注目すべき非常に注目すべきことは、インスタンスメソッドが実際
にSwiftのカリー化された関数であることです。
カリー化の主な考え方は、関数を部分的に適用できることです。つまり、関数が呼び出される前にパラメーター値の一部を決定できるということです。 部分関数アプリケーションは、新しい関数を生成します。
だから私はクラスを持っています:
class MyHelloWorldClass { func helloWithName(name: String) -> String { return "hello, \(name)" } }
helloWithName
クラスの関数を指す変数を作成できます。
let helloWithNameFunc = MyHelloWorldClass.helloWithName
私の新しい機能
helloWithNameFunc
のタイプの関数である
MyHelloWorldClass -> (String) -> Sting
、私のクラスの「インスタンス」をとり、そして今度は、文字列の値をとり、文字列値を返す、という別の関数を返します。
これで、次のように関数を呼び出すことができます。
let myHelloWorldClassInstance = MyHelloWorldClass() helloWithNameFunc(myHelloWorldClassInstance)("Mr. Roboto")
Init
:特記事項
class, struct
、または
enum
が初期化されると、特別な
init
メソッドが呼び出されます。
Swiftでは、他のメソッドと同様に初期化パラメーターを定義できます。
class Person { init(name: String) {
他のメソッドとは対照的に、
init
メソッドの
最初のパラメーターの
名前には、クラスのインスタンスを受け取るときに常に
外部名が必要です。
クラスインスタンスを読みやすくするために、内部名と外部名(この場合は
fromName
以外のパラメーターを追加することをお
fromName
ます。
class Person { init(fromName name: String) {
そして、もちろん、他のメソッドと同様に、
init
メソッドでパラメーターの外部名をスキップする場合は、_文字を追加できます。
Swift Programming Language bookの次の初期化例の読みやすさとパワーが気に入っています。
struct Celsius { var temperatureInCelsius: Double init(fromFahrenheit fahrenheit: Double) { temperatureInCelsius = (fahrenheit - 32.0) / 1.8 } init(fromKelvin kelvin: Double) { temperatureInCelsius = kelvin - 273.15 } init(_ celsius: Double) { temperatureInCelsius = celsius } } let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
外部パラメータ名をスキップする
ことは 、
class / enum / struct
初期化される
方法を無視したい場合にも役立ちます。
David Owen json-swiftライブラリでこれを使うのが本当に好き
です: public struct JSValue : Equatable {
「特別な」パラメーター
Objective-Cと比較して、
Swiftには関数/メソッドに渡すことができるパラメーターに関する追加オプションがあります。 以下に例を示します。
オプションのパラメーター
Swiftは、
Optional
型の新しい概念を導入します。
オプションは、「この値とxに等しい」または「値がまったくない」と言います。オプションは、Objective-Cのポインターでnilを使用するのと似ていますが、クラスだけでなくすべてのタイプで機能します。 オプションは、Objective-Cのnilポインターよりも安全で表現力があり、Swiftの多くの強力な機能の中心にあります。
このパラメータがあることを示すために
Optional
(すなわち、かもしれ
nil
)、単に疑問符を追加しますか? 型指定後:
func myFuncWithOptionalType(parameter: String?) {
Optionals
するときは、忘れずに「デプロイ」してください!
func myFuncWithOptionalType(optionalParameter: String?) { if let unwrappedOptional = optionalParameter { println("The optional has a value! It's \(unwrappedOptional)") } else { println("The optional is nil!") } } myFuncWithOptionalType("someString")
Objective-Cを使用している場合は、
Optionals
を使用するときに適応するのに時間がかかります。
Default
値を持つパラメーター
func hello(name: String = "you") { println("hello, \(name)") } hello(name: "Mr. Roboto")
デフォルト値(
default
)を持つパラメーターには、自動的に
外部名が
付けられることに注意してください。
関数を呼び出すときに
default
値を持つパラメーターをスキップできるため、
default
値を持つすべてのパラメーターをパラメーターリストの最後に配置することをお勧めします。 このトピックに関する
Swiftプログラミング言語の本から
の引用です:
関数パラメーターリストの最後に、デフォルト値を持つパラメーターを配置します。 これにより、関数のすべての呼び出しで、デフォルト以外の引数に同じ順序のパラメーターが使用されるようになります。 したがって、すべての場合で同じ関数が呼び出されます。
主に
default
パラメーターを使用するのが大好きです。これは主に、これによりコードの変更と下位互換性の提供が容易になるためです。 たとえば、現時点で必要な2つのパラメーターから開始できます。たとえば、カスタムの "テーブルセル"
UITableViewCell
構成したり、別のパラメーターが必要なときに追加の構成が必要な場合(たとえば、ラベルテキストの異なる色セル)、デフォルト値で新しいパラメーターを追加するだけです-この関数が既に呼び出されている他のすべての場所は変更せずに残し、新しいパラメーターを必要とするコードの新しい部分にカップルを転送できます デフォルト値以外の値を持つEMPL!
可変引数(可変数のパラメーター)
Variadic
は、要素の配列を渡すより読みやすいバージョンです。 実際、以下の例で内部パラメーター
name
のタイプを見ると、それがタイプ[String]であることがわかります。
func helloWithNames(names: String...) { for name in names { println("Hello, \(name)") } }
空の配列に対応する0の値を渡すことができるので、必要に応じて空の配列が渡されるかどうかを確認することを忘れないでください。
func helloWithNames(names: String...) { if names.count > 0 { for name in names { println("Hello, \(name)") } } else { println("Nobody here!") } } helloWithNames()
variadic
パラメータに関する別の注意:
variadic
パラメータは、パラメータリストの最後のパラメータでなければなりません!
入力パラメータ
inout
パラメーターを使用すると、参照渡しの外部変数を操作できます。
var name1 = "Mr. Potato" var name2 = "Mr. Roboto" func nameSwap(inout name1: String, inout name2: String) { let oldName1 = name1 name1 = name2 name2 = oldName1 } nameSwap(&name1, &name2) name1
これは、
Objective-Cのエラー管理スクリプトの非常に一般的なパターンです。
NSJSONSerialization
はそのような例の1つです。
- (void)parseJSONData:(NSData *)jsonData { NSError *error = nil; id jsonResult = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; if (!jsonResult) { NSLog(@"ERROR: %@", error.description); } }
Swiftはまだ非常に若いプログラミング言語であるため、エラー処理の明確な規則はありませんが、入力パラメーター以外にも多くの方法があります。 最近の
Swift David Owenエラー処理に関するブログ投稿を
ご覧ください 。 本
「Functional Programming in Swift」のこのトピックに関する多くの資料。
汎用オプション
この
ジェネリック投稿ではあまり注意を払いませんが、異なるタイプのパラメーターを受け入れる関数の非常に簡単な例を示しますが、両方のパラメーターは同じタイプです。
func valueSwap<T>(inout value1: T, inout value2: T) { let oldValue1 = value1 value1 = value2 value2 = oldValue1 } var name1 = "Mr. Potato" var name2 = "Mr. Roboto" valueSwap(&name1, &name2) name1
generics
詳細について
は、 AppleのSwift Programming Languageの
ジェネリックセクションをご覧になることをお勧めします。
パラメーター-変数
デフォルトでは、関数に渡されるパラメーターは定数であるため、関数のスコープ内で操作することはできません。 この動作を変更する場合は、
varキーワードを使用します。
var name = "Mr. Roboto" func appendNumbersToName(var name: String, #maxNumber: Int) -> String { for i in 0..<maxNumber { name += String(i + 1) } return name } appendNumbersToName(name, maxNumber:5) // Mr. Robot12345 name // Mr. Roboto
これは
inout
パラメーターとは完全に異なることに注意してください-変数パラメーターは外部から渡された変数を変更しません!
パラメータとして機能
スウィフトの機能は通常の変数として渡すことができます。 たとえば、関数には渡されたパラメーターとして別の関数がある場合があります。
func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> String) -> String { let luckyNumber = Int(arc4random() % 100) return lotteryHandler(name, luckyNumber) } func defaultLotteryHandler(name: String, luckyNumber: Int) -> String { return "\(name), your lucky number is \(luckyNumber)" } luckyNumberForName("Mr. Roboto", lotteryHandler: defaultLotteryHandler) // Mr. Roboto, your lucky number is 38
関数参照のみがパラメーターとして渡されることに注意してください。この例では、
defaultLotteryHandler
関数です。 関数は、このパラメーターが渡される関数を決定するときに、後で実行されます。
インスタンスメソッドは同じ方法で渡すことができます。
func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> String) -> String { let luckyNumber = Int(arc4random() % 100) return lotteryHandler(name, luckyNumber) } class FunLottery { func defaultLotteryHandler(name: String, luckyNumber: Int) -> String { return "\(name), your lucky number is \(luckyNumber)" } } let funLottery = FunLottery() luckyNumberForName("Mr. Roboto", lotteryHandler: funLottery.defaultLotteryHandler) // Mr. Roboto, your lucky number is 38
関数定義をもう少し読みやすくするために、関数の型エイリアスを作成することを検討してください(
Objective-Cの typedef
に似てい
typedef
):
typealias lotteryOutputHandler = (String, Int) -> String func luckyNumberForName(name: String, #lotteryHandler: lotteryOutputHandler) -> String { let luckyNumber = Int(arc4random() % 100) return lotteryHandler(name, luckyNumber) }
パラメータータイプとして名前のない関数(
Objective-Cのブロックなど)もあります。
func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> String) -> String { let luckyNumber = Int(arc4random() % 100) return lotteryHandler(name, luckyNumber) } luckyNumberForName("Mr. Roboto", lotteryHandler: {name, number in return "\(name)'s' lucky number is \(number)" }) // Mr. Roboto's lucky number is 74
Objective-Cでは、ブロックをパラメーターとして使用することは、非同期操作のあるメソッドで
completion handler
と
error handler
を制御するために非常に一般的です。
Swiftでも人気があります。
アクセス制御
Swiftには3つのレベルのアクセス制御があります。
パブリックアクセスにより、これらのエンティティが定義されているモジュールのソースコードファイル内のエンティティ、およびこれらのエンティティが定義されているモジュールをインポートする別のモジュールのソースコードファイル内のエンティティを使用できます。 通常、フレームワークの
パブリックインターフェイスを指定するときは、
パブリックアクセスを使用します。
内部アクセスにより、これらのエンティティが定義されているモジュールのソースファイル内のエンティティを使用できますが、それらが定義されているモジュール外のソースコードファイルでは使用できません。 通常、アプリケーションまたはフレームワークの内部構造を定義するときに
内部アクセスを使用します。
プライベートアクセスは、エンティティの使用を、そのエンティティが定義されているソースファイルに制限します。
プライベートアクセスを使用すると、特定の機能の実装の詳細が隠されます。
デフォルトでは、各関数と変数は
内部です。これを変更する場合は、個々のメソッドまたは変数の前に
privateまたは
publicキーワードを使用する必要があります。
public func myPublicFunc() { } func myInternalFunc() { } private func myPrivateFunc() { } private func myOtherPrivateFunc() { }
Rubyを使用する場合、
プライベート関数をクラスの一番下に配置し、マーカーで区切ります:
class MyFunClass { func myInternalFunc() { }
Swiftの将来のリリースには、他のプログラミング言語でのアクセス制御の動作と同様に、1つの
プライベートキーワードを使用して、以下のすべてのメソッドを
privateとして示すオプションが含まれることを期待しています。
「特別な」戻り型
Swiftでは、関数によって返される型と値は、
Objective-Cで使用するよりも少し複雑になる可能性があります。特に、
Optionals
と複数の戻り値型が導入されています。
オプションの戻り型
関数が
nil
返す可能性がある場合、戻り値の型を
Optional
として定義する必要があります。
func myFuncWithOptonalReturnType() -> String? { let someNumber = arc4random() % 100 if someNumber > 50 { return "someString" } else { return nil } } myFuncWithOptonalReturnType()
もちろん、
Optional
戻り値を使用する場合は、忘れずに「展開」してください。
let optionalString = myFuncWithOptonalReturnType() if let someString = optionalString { println("The function returned a value: \(someString)") } else { println("The function returned nil") }
私が見た最高のオプションの説明は、
@ Kronusdark twitterから取ったもの
です。シュレディンガーの猫に似た@SwiftLangオプションがついに手に入りました! 猫を使用する前に、生きているかどうかを確認する必要があります。
多くの戻り値
Swiftの最も印象的な機能の1つは、多くの戻り値を持つ機能です。
func findRangeFromNumbers(numbers: Int...) -> (min: Int, max: Int) { var min = numbers[0] var max = numbers[0] for number in numbers { if number > max { max = number } if number < min { min = number } } return (min, max) } findRangeFromNumbers(1, 234, 555, 345, 423)
ご覧のとおり、戻り値のセットは、グループ化された値の非常に単純な構造であるタプルとして返されます。 タプルで複数の戻り値を使用するには、2つの方法があります。
let range = findRangeFromNumbers(1, 234, 555, 345, 423) println("From numbers: 1, 234, 555, 345, 423. The min is \(range.min). The max is \(range.max).")
多くの戻り値とオプション
返される値が
Optional
場合、複数の値を返すときに混乱が発生しますが、この状況を管理するには2つの方法があります。
上記の機能に関して、私のロジックは失火します。入力に何も送信されない場合、プログラムは異常終了します。
値が関数の入力に渡されない場合、戻り値を完全にオプションにすることができます:
func findRangeFromNumbers(numbers: Int...) -> (min: Int, max: Int)? { if numbers.count > 0 { var min = numbers[0] var max = numbers[0] for number in numbers { if number > max { max = number } if number < min { min = number } } return (min, max) } else { return nil } } if let range = findRangeFromNumbers() { println("Max: \(range.max). Min: \(range.min)") } else { println("No numbers!") }
他の場合では、タプル全体を
Optional
にするのではなく、タプル内の個々の値を
Optional
にすることが理にかなっています。
func componentsFromUrlString(urlString: String) -> (host: String?, path: String?) { let url = NSURL(string: urlString) return (url.host, url.path) }
タプルの値の一部がであると判断した場合、値のOptional
各組み合わせを個別に考慮する必要があるため、それらを「拡張」するのが少し難しくなりOptional
ます。 let urlComponents = componentsFromUrlString("http://name.com/12345;param?foo=1&baa=2#fragment") switch (urlComponents.host, urlComponents.path) { case let (.Some(host), .Some(path)): println("This url consists of host \(host) and path \(path)") case let (.Some(host), .None): println("This url only has a host \(host)") case let (.None, .Some(path)): println("This url only has path \(path). Make sure to add a host!") case let (.None, .None): println("This is not a url!") }
ほら
Objective-Cほど簡単ではありません!関数の戻り
スウィフト任意の関数は、関数を返すことができます。 func myFuncThatReturnsAFunc() -> (Int) -> String { return { number in return "The lucky number is \(number)" } } let returnedFunction = myFuncThatReturnsAFunc() returnedFunction(5)
読みやすくするために、返す関数の型エイリアスを使用できます。 typealias returnedFunctionType = (Int) -> String func myFuncThatReturnsAFunc() -> returnedFunctionType { return { number in return "The lucky number is \(number)" } } let returnedFunction = myFuncThatReturnsAFunc() returnedFunction(5)
入れ子関数
この投稿ではこのトピックを作成しませんが、Swiftでは関数内に関数がある可能性があることを知っておくと便利です。 func myFunctionWithNumber(someNumber: Int) { func increment(var someNumber: Int) -> Int { return someNumber + 10 } let incrementedNumber = increment(someNumber) println("The incremented number is \(incrementedNumber)") } myFunctionWithNumber(5)
終わりSwift .
Swift , : . , !
Swift , , . , ,
Swif t,
Swift .
Swift !