When I cmd click the split function in Xcode, it takes me to the header file. This is what it reads
public func split(maxSplit: Int = default, allowEmptySlices: Bool = default, #noescape isSeparator: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.SubSequence]
How does the following implementation work with above declaration?
someString.characters.split { $0 == "." }
Let's break it down:
public func split(maxSplit: Int = default, allowEmptySlices: Bool = default, #noescape isSeparator: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.SubSequence]
maxSplit: The first parameter, maxSplit, allows you to specify the maximum number of pieces that the sequence will be split into. The default is Int.max.
allowEmptySlices: The second parameter, allowEmptySlices specifies whether or not two consecutive separators in the sequence should lead to an empty slice. The default is false. For example if you had a String, "A..B", and you split on the . character, you could end up with either two (["A", "B"]) or three (["A", "", "B"]) items in the output array depending on what you pass for this parameter.
isSeparator: The last parameter is the closure that you pass to identify where to split the sequence.
Since both maxSplit and allowEmptySlices have default arguments, you don't need to include them in your function call unless you want to change them. The only parameter that you have to supply is the isSeparator closure.
In your case, you called:
someString.characters.split { $0 == "."}
...which is the equivalent of:
someString.characters.split(maxSplit: Int.max, allowEmptySlices: false) { $0 == ".' }
You could also write your function call like this:
someString.characters.split(isSeparator: { $0 == "." })
The way that you have written it makes use of the "trailing closure" syntax. If a function takes a closure as it's final argument, you can move the closure outside the parentheses like this:
someString.characters.split() { $0 == "." }
And if the function takes only one argument (not counting any default arguments that you are not supplying) then you can omit the parentheses altogether:
someString.characters.split { $0 == "." }
At the highest level, what happens is that split iterates through the sequence of characters. It tests each character using the supplied closure, and if the closure returns true, it splits the sequence on that character. In your case it will split the sequence of characters every time it locates a ".".
Some other notes:
rethrows: The whole function is marked rethrows. It will throw an error, but only if the closure that you pass for the isSeparator argument throws an error itself. Note that the isSeparator parameter allows you to pass a closure that throws an error, but you don't have to. Any time a function accepts a closure that throws an error, it will also accept a closure that does not throw. This is because non-throwing functions are a sub-type of throwing functions.
#noescape: The isSeparator parameter is marked #noescape. That simply means that nothing in the closure will survive past the end of the call to split.
Your line someString.characters.split { $0 == "."} uses the default values for maxSplit and allowEmptySlices and specifies a custom closure for isSeparator. Some longer version of calling the function split is:
let arr = str.characters.split(Int.max, allowEmptySlices: false) { (char) -> Bool in
char == "."
}
Since the above code uses the same values as the actual defaults (Int.max and false) you can remove the first two parameters and since the closure is the last parameter (making it a trailing closure) you can omit the entire parameter and simply write a closure in the following way:
let arr = str.characters.split { (char) -> Bool in
char == "."
}
Now we can simplify the closure by omitting the specific signature:
let arr = str.characters.split { $0 == "." }
someString.characters.split { $0 == "."}
is equivalent to
someString.characters.split(isSeparator: { $0 == "."})
the closure in your code is called a trailing closure, closures that are the last parameter to a function can be placed after the function declaration, and if no other parameters remain, you can also omit ().
The first two parameters have default values, so they can be omitted, leading to the simplified call you posted.
Related
Playground
XCode Version 13.3 (13E113)
Swift 5.6
First print of compactMap() closure displays this:
["What\'s", "Going", "On?"]
The second print displays this:
[(), (), ()]
Seems like if I declare anything inside the closure, the output of the closure changes.
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
lineText
})
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
let _ : String
lineText
})
Is there a way to wrap some of it to hide the other declaration?
Is the closure confused about the type?
Is there any way to unconfuse it (or me)?
Is it a Swift bug?
Issue is root of why other things I'm trying to with this pattern aren't working.
UPDATE
As per #Shadowrun's answer below, I added a return statement in the 3rd example, but that leads to compile time errors. So is that resolvable?
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
let _ : String
return lineText
})
expression failed to parse:
error: test playground.playground:38:52: error: generic parameter 'ElementOfResult' could not be inferred
print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
^
Swift.Sequence:2:28: note: in call to function 'compactMap'
#inlinable public func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]</b>
If you have a one line closure there's an implicit return, so the first one is returning lineText which is of type string. See "Functions With an Implicit Return" at https://docs.swift.org/swift-book/LanguageGuide/Functions.html
The second one doesn't actually return anything, once it is more than one line, there is no implicit return. So the return type is Void which is also spelled as the empty tuple, () and that's what you get there.
You need to say return lineText explicitly if you mean to return something.
This function:
{ idx, lineText in
let _ : String
lineText // This line evaluates the value of lineText and does nothing with the result
}
does not return a value. Any function that doesn't return a value returns a Void value. Void is a type with only one possible value, called (). Mapping to void is a pointless thing to do. Even if your function did a side effect, like printing something, it wouldn't be good style to use map just for side effects.
You can guard against this kind of mistake by being more explicit about the return type in the closure
{ idx, lineText -> String in ... }
For example, I have func test(paramA: String){}.
Sometimes I want to pass a string as argument but other times, I definitely don't want to have an argument at all, but rather: test()
Is it possible to call both test() and test("hello") or does it need to be different functions?
Also I'm not sure if this is called something in particular. SO defined optional parameters as:
An optional parameter is one that a caller can include in a call to a function or method, but doesn't have to. When omitted, a default value is used instead. Optional parameters are useful when the default value is used in most cases, but still needs to be specified on occasion.
To me, using Swift, optional means using ? in reference to it perhaps being nil.
EDIT
Thanks for the response. It's evident that I should use default parameters. However, doing so in a closure result in the following error:
"Default argument not permitted in a tuple type"
func characterIsMoving(i: Int, j: Int, completion: #escaping(_ moveWasWithinLimit: Bool, _ test: Bool = false ) -> Void) { ... }
Here comes the full function if that's helpful:
func characterIsMoving(i: Int, j: Int, completion: #escaping(_ moveWasWithinLimit: Bool, _ test: Bool = false ) -> Void) {
if !gameBoardArray[i][j].isAccessibilityElement {
print("Not Accessible")
currentSelectedTile = character.getCurrentPosition()
return
}else {
print("Moving")
var moveWasWithinLimit: Bool
if(characterMoveLimit.contains(currentSelectedTile)){
print("Within Limit")
previousSelectedTile.fillColor = SKColor.gray
currentSelectedTile.fillColor = SKColor.brown
placeCharacter(row: i, col: j)
buttonIsAvailable = false
for moveRadius in characterMoveLimit {
moveRadius.fillColor = SKColor.gray
}
characterMoveLimit.removeAll()
moveLimit(limitWho: "CHARACTER", characterOrEnemy: character, i: i, j: j)
moveWasWithinLimit = true
completion(moveWasWithinLimit)
}else{
print("Outside Move Limit")
currentSelectedTile = previousSelectedTile
moveWasWithinLimit = false
completion(moveWasWithinLimit)
}
}
}
You (everyone, really) would really benefit from reading the Swift book, cover to cover.
What you're looking for is called a default value.
func test(paramA: String = "Your default value here") {}
or
func test(paramA: String? = nil) {}
The former is simpler, but more limited. For example, you can't distinguish rather the default value "Your default value here" was used, or whether the caller passed in their own value, which happens to be "Your default value here"). In my experience, the distinction is seldom required, but it's good to call out just in case.
In the latter case, you have the flexibility to handle the optional in many more ways. You could substitute a default value with ??, do conditional binding, map it, etc.
First you have to be clear with your requirements.
You should not use methods which are directly dependent on instance variables, as setting there values will be a problem according to your unit test cases.
As per my understanding this is the correct approach you can define the method with parameter having default value.
So that you can call that method as per your requirements with or without the parameter.
I am wondering why map format has to be {( )} rather than just { }
func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
// the following is right
var num1Reduce = nums1.reduce(0){ $0 + $ 1}
/// the following is wrong ??
var num2Dict = Dictionary(nums2.map{ $0, 1 }, uniquingKeysWith : +)
// the following is right
var num1Dict = Dictionary(nums1.map{ ($0, 1) }, uniquingKeysWith : +)
}
and I even see the following format ({ }). I am totally confused!
let cars = peopleArray.map({ $0.cars })
print(cars)
You are using the following Dictionary initializer:
init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Dictionary<Key, Value>.Value, Dictionary<Key, Value>.Value) throws -> Dictionary<Key, Value>.Value) rethrows where S : Sequence, S.Element == (Key, Value)
Note that S is a sequence where its elements are a tuple of key/value pairs.
When you pass nums1.map{ ($0, 1) } to the first parameter, you are creating an array of key/value tuples from nums1.
It fails when you use nums2.map{ $0, 1 } because that is missing the parentheses for the tuple.
Keep in mind that nums1.map{ ($0, 1) } is shorthand for nums1.map({ ($0, 1) }). That's all related to trailing closures which has nothing to do with the parentheses for the tuple that appear inside the { }.
A map is a function that takes a closure as a parameter. We can call the map and pass the parameter like we do for any other ordinary function call without removing the brackets ()e.g
(0...100).map ({ _ in print("yeti")})
But swift allows us to remove the brackets as a way of shorthanding and we can write it like, hence eliminating the ()
(0...100).map { _ in print("yeti")}
But incase you want to access individual values of the array elements, you can do so in two ways,
Given an array, you can access it's individual element using $0, which basically says, Hey map, give me the first element at this current index.
(0...100).map {$0}
Instead of using the default swift indexing, you decide to define the value you are accessing by giving it a readable variable name e.g
(0...100).map {element in}
This gets $0 and assigns it to element, the in keyword basically tells the compiler that hey, $0 is now element and we are going to use it after in. Otherwise if you remove the in keyword, the compiler says it doesn't know any variable called element.
For special collections like dictionaries, they have two values per index, i.e the key and value, therefore if you want to access the contents of a dictionary during the mapping, you can do it in two ways like above, a). use the default swift indexes, or give the values per index, readable variable names. e.g
let dictionary = ["a": 3, "b": 4, "c": 5]
dictionary.map{($0, $1)}
We use inner brackets () to let the compiler know that the collection we are mapping over has two values per index. Please note the inner parenthesis are creating a tuple
dictionary.map {(key, value) in }
I'm writing a little program that reads and writes text files into an NSTableView. I had the reading working fine, and then moved onto the writing. And I got...
FR Optional(0) Optional(0) Optional(0) Optional(0) Optional(46.29) Optional(0)
I understand why this is happening: the values are NSNumbers in a dictionary, so they are, by definition, optional. But obviously this is not useful output.
Is there an easy way to output the value without the Optional and any similar bumpf?
The reason why you see Optional(n) is because you're printing the optional without unwrapping.
I suggest you re-read the optionals chapter in the Apple book to get a better grasp at why this is happening to you.
The short version is, an Optional is a type, in fact if you look at the Swift source code, you will find that's it's just an Enum!
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
/// Construct a `nil` instance.
public init()
/// Construct a non-`nil` instance that stores `some`.
public init(_ some: Wrapped)
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
#warn_unused_result
public func map<U>(#noescape f: (Wrapped) throws -> U) rethrows -> U?
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
#warn_unused_result
public func flatMap<U>(#noescape f: (Wrapped) throws -> U?) rethrows -> U?
/// Create an instance initialized with `nil`.
public init(nilLiteral: ())
}
So if your values are Optionals, you have to unwrap them to see their values.
Take a look at this code and try to guess the output:
var code: String? = "hello"
if let code = code where code == "hello" {
print(code)
}
var upperCase = code?.uppercaseString
print(upperCase)
Output:
Did you figure it out?
It looks like this:
hello
Optional("HELLO")
Why does the first hello print ok?
Because it's being unwrapped in the if let statement.
But the second one, upperCase is never unwrapped, so an Optional will remain optional (unless unwrapped). code?.uppercaseString returns a new Optional. By printing it directly, we get what you see.
If you want to extract the value the optional is holding you have two operators. ? and !.
The first one is usually preferred, but if you're sure that the value is ok, you can use force the unwrap by using !, so you could do
print(uppercase!)
Careful tho, because if upperCase happens to be nil, you'd get a runtime crash.
If the values you write are held in an (optional) array, then .flatMap, prior to printing, will do the trick for you
// myOptionalDoubles : [Double?]
myOptionalDoublesForWriting = myOptionalDoubles.flatMap { $0 }
Using .flatMap like this, you unwrap any non-nil values in the array myOptionalDoubles, and return an array myOptionalDoublesForWriting of type [Double] (non-optional). Any nil entries in myOptionalDoubles will not follow to myOptionalDoublesForWriting.
If you don't want to lose information about optional entries, you can use .map instead:
// myOptionalDoubles : [Double?]
var myOptionalDoublesForWriting = myOptionalDoubles.map { $0 ?? 0.0 }
In this case, you make use of the nil coalescing operator to either unwrap each (non-nil) array entry, or, if it's nil, give it value 0.0. As above, the mapped to array myOptionalDoublesForWriting will be of non-optional type [Double].
I decided to update a project that I had made during Swift beta 1 now that Swift 1.0 is out. There's a particular issue, however, that I can't seem to fix that came up as a result of the updated array semantics and inout keyword. Consider the following two functions (you can just paste them into a playground):
func bubbleSort<T : Comparable>(inout arr : [T]) {
var numSwaps = 0
do {
numSwaps = 0
for i in 0..<(arr.count - 1) {
if arr[i] > arr[i + 1] {
(arr[i], arr[i + 1]) = (arr[i + 1], arr[i])
numSwaps++
}
}
} while numSwaps != 0
}
func testFunc(sortFunc: ([Int]) -> ()) {
sortFunc([5,4,3,2,1])
}
I can't seem to make the two work together. I tried using shorthand:
testFunc {bubbleSort(&$0)}
I also tried without shorthand:
testFunc { (arr: [Int]) -> () in
bubbleSort(&arr)
}
In either case, I get errors:
Is this a bug, or am I screwing something up? Thanks in advance!
What you're running into is that method parameters are by default immutable and in-out functions are mutating. You can't use the closure shorthand, because you'll need to declare the closure's argument as mutable with the var keyword, like so:
func testFunc(sortFunc: ([Int]) -> ()) {
sortFunc([5,4,3,2,1])
}
testFunc { (var arr: [Int]) -> () in
bubbleSort(&arr)
}
Now you can call bubbleSort with arr as the in-out parameter.
What you're running into is the change in beta 3 (if I recall correctly) where changing en element of an array became a mutating operation. Before that changing an element was a non-mutating operation, and therefore you can change an element on an Array constant; you just couldn't change its length. But since beta 3, to change an element on an array, it needs to be non-constant.
bubbleSort takes the array by reference (inout) because it needs to change the elements of the array and have it visible to the caller (prior to beta 3, it just took it by value). You can only pass a non-constant variable by reference. In the closure you wrote, arr is a constant (it's an array parameter not declared var or inout), therefore you cannot pass it by reference.
The obvious answer is that arr needs to be var or inout. Which one you need depends on what these functions are intended to do. What you have currently is discard the result of calls, which is pointless, so you are clearly not showing us what these functions are supposed to do.
#NakeCook's answer, simply make arr a var, makes it possible to pass it by reference, but it doesn't change the fact that the closure takes its argument by value (and the fact that sortFunc in testFunc is a pass-by-value function. That means testFunc gives an array to the function to sort, but does not care or want changes to be reflected back to it. If this is what you want, then that is the answer.
However, if testFunc is supposed to have an array, pass it to a sort function to sort, and wants to see changes to the array in its scope, then it needs to do something else:
func testFunc(sortFunc: (inout [Int]) -> ()) {
var x = [5,4,3,2,1]
sortFunc(&x)
}
testFunc { (inout arr: [Int]) -> () in
bubbleSort(&arr)
}
In this case, the closure's parameter is declared as inout (so it's passed by reference), and also the sortFunc (in testFunc)'s type explicitly mentions that it takes its parameter by reference.