I studied for myself, so make a note
import Foundation
//Processing can be unified
//Functions are a type of closure
//Maintenance and readability can be improved by cutting out and writing the process.
// function
/*
func function name(Argument name: Type, Argument name: Type ...) ->Return type{
Statement executed when a function is called
Return the return value with a return statement if necessary
}
*/
func double(_ x: Int) -> Int {
return x * 2
}
double(2) // 4
//Execution method
//let constant name = function name (argument name 1: argument 1, argument name 2: argument 2...)
func functionWithDiscardResult() -> String {
return "Discardable"
}
_ = functionWithDiscardResult() // Discardable
//argument
func printInt(_ int: Int) {
print(int)
}
printInt(1) // 1
//External argument name and internal argument name
//External argument = used when calling
//Internal argument = what is used in the function
/*
func function name (internal argument: type, external argument internal argument: type){}
*/
func invite(user: String, to group: String) {
print("\(user) is invited to \(group)")
}
invite(user: "tanaka", to: "kendo club")
//Omission of external arguments_Use this
func sum(_ int1: Int, _ int2: Int) -> Int {
return int1 + int2
}
sum(10, 10) // 20
//Default argument
//You can put anything
func greet(user: String = "Anonymos") {
print("Hello \(user)")
}
greet() // Hello Anonymos
greet(user: "tanaka")
//It has three arguments that specify the search word, sort key, and ascending order,
//Since all you need is a search keyword, you can set defaults for unnecessary arguments, so
//If you want to search by just the search keyword, or if you want to search by specifying the sort key etc., you can handle it with one function.
func search(byQuery query: String,
sortKey: String = "id",
ascending: Bool = false) -> [Int] {
return [1, 2, 3]
}
search(byQuery: "query")
//In-out argument
//When reassigning to an argument inside a function outside the function, add inout or an error
//Can be entered as a temporary value
//Add & to call the inout argument
//Error when not attached: Passing value of type'String' to an inout parameter requires explicit '&'
func greetA(user: inout String) {
if user.isEmpty {
user = "Anonymos"
}
print("Hello \(user)")
}
var user: String = ""
greetA(user: &user)
//Variadic argument Argument that receives an arbitrary number of values
//Merit: Do not make the caller of the function aware that it is an array
//Up to one in one function
//Become an Array type
func printA(strings: String...) {
if strings.count == 0 {
return
}
print("first: \(strings[0])")
for str in strings {
print("element: \(str)")
}
}
printA(strings: "abc", "def","ghi")
//first: abc
//element: abc
//element: def
//element: ghi
//Argument check
func stringA(from: Int) -> String {
return "\(int)"
}
let int = 1
let double = 1.0
let str1 = stringA(from: int)
// let str2 = stringA(from: double) // error
// Cannot convert value of type 'Double' to expected argument type 'Int'
//Return value
//no return
func greetB(user: String) {
print("Hello \(user)")
}
greetB(user: "tanaka")
//Same as above
func greetC(user: String) -> Void {
print("Hello \(user)")
}
greetC(user: "tanaka")
//Implicit return
//If only the return value, return can be omitted.
func makeMassage(toUser user: String) -> String {
"Hello, \(user)"
}
//This is an error
// Missing return in a function expected to return 'String'
/*
If there is something other than the return value, return cannot be omitted.
func makeMassage1(toUser user: String) -> String {
print(user)
"Hello, \(user)"
}
*/
//closure
/*
Processing of a set of variables and constants in scope
{ (Argument 1: type, argument 2: type)->Return value in
Statement executed when closure is executed
Return the return value with a return statement if necessary
}
*/
//Takes one Int type argument and returns the Int type return value that is doubled (Int) → Int type closure is executed
let doubleA = {(x: Int) -> Int in
return x * 2
}
doubleA(2) // 4
//Closure types can be handled in the same way as normal ones, so they can also be used as variable and constant types and function arguments.
let closure: (Int) -> Int
func someFunctionAA(x: (Int) -> Int) {}
closure = { int in
return int * 2
}
closure(1)
//Type inference
var closureA: (String) -> Int
//When the argument and return value are specified
closureA = { (str: String) -> Int in
return str.count
}
closureA("abc") // 3
//If omitted
closureA = { str in
return str.count * 2
}
closureA("abc") // 6
//Error if the type is not decided
// Unable to infer type of a closure parameter 'str' in the current context
//let butClosure = { str in
// return str.count * 2
//}
//Execution method
//Calling method is the same as function
//Add () to the end of the variable name or constant name to which the closure is assigned, and enter the argument in ().,’Arrange by delimiter
//The return value is assigned to a variable or constant
//let constant name = variable name (argument 1, argument 2)
//Variables and constants to which closures are assigned are type inferred as closure types.
//Counting the number of strings
//The same thing is done, but the writing style is different
let lengthOfString = { (string: String) -> Int in // (String) -> Int
return string.count
}
lengthOfString("abcdefghijklmnopqrstuvwxyz") // 26
func lengthOfStringA(_ string: String) -> Int {
return string.count
}
lengthOfStringA("abcdefghijklmnopqrstuvwxyz") // 26
//argument
//Closures cannot use external and default arguments
//Same as omitting arguments
let add = { (x: Int, y: Int) -> Int in
print(x + y)
return x + y
}
add(1,2) // 3
func addFunc(_ x: Int, _ y: Int) -> Int {
print(x + y)
return x + y
}
addFunc(1, 2) // 3
//Short argument Omission of argument name
// $0 or something$1 can be used
let isEqual: (Int, Int) -> Bool = {
return $0 == $1
}
isEqual(1,1) // true
isEqual(2,1) // false
let keisan: (Int, Int) -> Int = {
return $0 + $1
}
keisan(1,2) // 3
//When using abbreviated arguments, the argument type is not specified in the closure definition, so an error will occur if the type cannot be inferred from the outside.
//If you use it carelessly, it tends to be a code with low readability that you do not know what the argument means,
//It can be very simple, so it is better to use it positively
let numbers = [10,20,30,40]
//official
// func filter(_ isIncluded: (Int) throws -> Bool) rethrows -> [Int]
// filter(isIncluded: (Int) throws -> Boo)
//The following is the same as what you are doing, but it is easy to read because it is omitted
let aaaaaa = numbers.filter { val -> Bool in
val > 20
}
let bbbbbb = numbers.filter { val in
val > 20
}
let moreThenTwenty = numbers.filter { $0 > 20 }
aaaaaa // [30, 40]
bbbbbb // [30, 40]
moreThenTwenty // [30, 40]
//Return value
//None
let emptyReturnValueClosure: () -> Void = {}
//There is one
let singleReturnValueClosure: () -> Int = { return 1 }
// ()And Void are the same, but in the case of closures, use Void if there is no return value
// ❌ () -> ()
// ❌ Void -> Void
// ⭕️ () -> Void
//Capturing variables and constants with closures
/*
Variables and constants normally defined in the local scope cannot be used outside the local scope,
The variables and constants referenced in the one-step closure
Even if the scope in which the closure is executed is other than the local scope in which variables and constants are defined
Available when closing closures
This is because closures hold references to variables and constants in their own defined scope.
This is called capture
*/
let greeting: (String) -> String
/*-----------------------------*/
do {
let symbol = "!"
greeting = { user in
return "Hello, \(user)\(symbol)"
}
}
greeting("ken") // "Hello, ken!"
// symbol :::error because symbol is defined in another scope
// Cannot find 'symbol' in scope
//Since the constant greeting is declared outside the do scope, it can be used outside the do,
//The constant symbol cannot be used outside because it is declared within the scope of do.
//However, the closure assigned to the constant greeting can be executed outside do even though it uses symbol internally.
//This is achieved by the closure holding a reference to a variable or constant in its own defined scope by capture.
//The target of capture is not the value contained in the variable or constant, but the variable or constant confidence.
//Therefore, changes to the captured variables will also be reflected in the closure execution.
//Example
//Below, the closure assigned to the constant counter increments the value of the variable count by 1 each time it is executed.
//The initial value of the variable count is 0, but since it captures the variable itself, updates to the variable are also retained.
//Therefore, it will be a different value each time it is executed.
let counter: () -> Int
do {
var count = 0
counter = {
count += 1
return count
}
}
counter() // 1
counter() // 2
counter() // 3
counter() // 4
counter() // 5
//Closure as an argument
//As a specification that is valid only when using a closure as an argument of a function or another closure
//Has attributes and trailing closures
//Attributes are additional information you specify for closures
//Trailing closure is a specification to improve the readability of functions that take arguments
//Specifying attributes
/*
There are two attributes
- escaping
- autoclosure
func function name(Argument: @attribute name closure type name){
Statement executed when a function is called
}
*/
//func or(_ lhs: Bool, _ rhs: @autoclosure () -> Bool) -> Bool {
// if lhs {
// return true
// } else {
// return rhs()
// }
//}
//
//or(true, false) // true
// escaping
//Closures that run asynchronously
/*
An attribute that indicates that the closure passed as a function argument may be kept outside the scope of the function.
The compiler determines if the closure needs to capture based on the presence or absence of the escaping attribute.
If closures can be kept outside the scope of the function, that is, if the escaping attribute is required
A capture is required because it must be kept outside the scope of the function until the closure is executed.
*/
var queue = [() -> Void]()
//Adds the closure given as an argument to the array queue
//In other words, the closure of this argument is kept out of scope
//Therefore, it is necessary to specify the escaping attribute in the argument of the enqueue function.
//If not specified, a compile error will occur.
func enqueue(operation: @escaping () -> Void) {
queue.append(operation)
}
enqueue {
print("executed")
}
enqueue {
print("executed")
}
queue.forEach { $0() }
//Closures without the escaping attribute cannot be kept outside the scope of the function
//Closure execution must be done within the scope of the function
//The closure passed to the executeTwice function does not have the escaping attribute, but it is executed only within the scope of the function, so a compile error does not occur.
func executeTwice(operation: () -> Void) {
//Running here
operation()
operation()
}
executeTwice {
print(("executed"))
}
//autocloseure Lazy evaluation using closures
//Attribute to realize lazy evaluation by wrapping the argument in closure
//Or function that takes two Bool type arguments and returns the OR
//Find the logical sum || A function that behaves the same as the operator
func or(_ lhs: Bool, _ rhs: Bool) -> Bool {
if lhs {
print("true")
return true
} else {
print(rhs)
return rhs
}
}
or(true, false)
//The first argument is lhs()
//Return rhs to the second argument
//Inside the lhs and rhs functions, we run print so that we can see if each function has been executed.
func orA(_ lhs: Bool, _ rhs: Bool) -> Bool {
if lhs {
print("true")
return true
} else {
print(rhs)
return rhs
}
}
func lhs() -> Bool {
print("lhs()Was executed")
return true
}
func rhs() -> Bool {
print("rhs()Was executed")
return false
}
orA(lhs(), rhs())
//Trailing closure Notation that describes the closure of the argument in ()
/*
Trailing closure is a notation that can be done by writing a closure at the base of () when the last argument of the function is a closure.
*/
func execute(parameter: Int, handler: (String) -> Void) {
handler("parameter is \(parameter)")
}
//Without trailing closure
execute(parameter: 1, handler: { string in
print(string)
})
//When using a trailing closure
execute(parameter: 2) { print($0) }
//If there is only one argument, the function call () can also be omitted.
func executeA(handler: (String) -> Void) {
handler("executed")
}
//the same
executeA { valure in
print(valure)
}
//the same
executeA { print($0) }
//Function as a closure
/*
Since a function is a type of closure, it can be treated as a closure. To use a function as a closure, refer to the function only by the function name.
By treating a function as a closure, you can assign it to a function, variable, or constant, or pass an argument to another function.
let constant name=Function name
The expression can even include arguments, multiply by
let constant name=Function name(Argument name 1:Argument name 2:)
*/
func doubleA(_ x: Int) -> Int {
return x * 2
}
let function = doubleA
function(43)
//By defining the closure that is the argument of the function as a function, you can combine duplicate closures or combine them.
//You can give the closure a meaningful name
//Consider the case where the closure that is the argument of the function is not defined as a function.
// Array<Element>Using the type map,{ $0 * 2 }I use the closure twice
//The map method returns a new collection, each transformed by the processing specified by the closure.
let array1 = [1,2,3]
let doubleArray1 = array1.map { $0 * 2 }
let array2 = [4,5,6]
let doubleArray2 = array2.map { $0 * 2 }
doubleArray1
doubleArray2
//By treating the function as a closure, the above example can be rewritten as:
func doubleB(_ x: Int) -> Int {
return x * 2
}
let array1B = [1,2,3]
let doubleArray1A = array1B.map(doubleB)
let array2B = [4,5,6]
let doubleArray2A = array2B.map(doubleB)
//Duplicate by defining closures as functions{ $0 * 2 }Put together the process in one place
//Since it can be cut by giving the content of the process called doubleB, it becomes a more readable code.
//Initialization of variables and constants using closure expressions
//Closure expressions can sometimes make it easier to understand the initialization of complex values.
//Suppose you want to implement a type that represents a 3x3 grid.
//The mass is modeled in a two-dimensional array, and each value is directly defined literally.
var board = [ [1,1,1], [1,1,1], [1,1,1] ]
board
//Instead of defining this two-dimensional array directly, consider describing the procedure for generating a two-dimensional array.
//Generate 3x3 squares by generating 3 rows with 3 1s
// Array<Element>Type initializer init(reqwationg:count)Generates a value with repeating arguments added by the number of counts
var boardA = Array(repeating: Array(repeating: 1, count: 3), count: 3)
boardA // [[1, 1, 1], [1, 1, 1], [1, 1, 1]]
//Now you don't have to specify each element of the array.
//Array to combine variable initialization expressions into one<Element>Nesting the formulas for type generation made it difficult to figure out the structure.
//Also. It becomes necessary to manage the fixed value of 3 in two places.
//Closure expressions can be used to implement a series of initialization procedures into a single expression. In the following example, each nested expression can be used to implement a series of initialization procedures. Can be one formula,
//In the following example, the nested expressions are assigned to the constants row and board, respectively, making it easier to understand how each is generated.
//Also, since the number of squares on one side is also used as a constant after being declared, management can be unified.
var boardB: [[Int]] = {
let sideLength = 3
let row = Array(repeating: 1, count: sideLength)
let board = Array(repeating: row, count: sideLength)
return board
}()
boardB // [[1, 1, 1], [1, 1, 1], [1, 1, 1]]
//By doing the above, even if the initialization process of variables and constants is complicated, it will be easier to understand how the initial values are generated.
Recommended Posts