Perhaps this is an Xcode 8 beta issue, however, prior to 2.2 the var keyword is allowed to prepend parameters in function signatures:
func (var stringName: String) { ... }
This is has since been deprecated in lieu of there being little benefit over inout
func (stringName: inout String) { ... }
I've attempted the following in a map closure, and though I don't receive a deprecation warning as mildly expected I should, the error was rather a segmentation fault: 11
let demoString = ["hi", "there", "world"].map { (var word) -> String in
let firstChar = word.remove(at: word.startIndex)
}
The error kicks in as soon as I attempt to mutate the (assumedly mutable) word variable.
I've attempted other variation e.g. using inout
let demoString = ["hi", "there", "world"].map { (word: inout String) -> String in
let firstChar = word.remove(at: word.startIndex)
}
But the compiler complains that this erroneously changes the signature of the closure altogether and won't compile.
Obviously, the workaround is simply to copy the variable to a local one within the closure:
let demoString = ["hi", "there", "world"].map { (word) -> String in
let tempWord = word
let firstChar = tempWord.remove(at: tempWord.startIndex)
}
However, I am interested in knowing if this is expected functionality & whether or not there is a way of mutating a parameter passed into a closure directly?
Closures can mutate inout arguments just fine:
var str1 = "Pine"
var str2 = "Juniper"
let closure = { (s1: inout String, s2: inout String) -> Void in
s1 = "Oak"
s2 = "Chestnut"
}
closure(&str1, &str2)
print(str1, str2)
The problem you are facing is Array.map doesn't have a method signature that includes an inout parameter.
The only way around this that I can think of is to extend Array and add the map method yourself:
extension Array {
func map<T>(_ closure: (inout T) -> Void) -> Array<T> {
var arr = [T]()
for i in 0..<self.count {
var temp : T = self[i] as! T
closure(&temp)
arr.append(temp)
}
return arr
}
}
var hi = "hi", there = "there", world = "world"
var demoStrings = [hi, there, world]
var result = demoStrings.map { (word: inout String) in
word.remove(at: word.startIndex)
}
print(result) // ["i", "here", "orld"]
As per SE-0003 var parameters no longer exist in Swift 3.
Fundamentally, you shouldn't be mutating the parameters given from map, anyway. Instead: use non-mutating functions, or make a local mutable copy.
In this case, there's no reason to be using remove(_:) just to get the first character. This would require copying a String's memory (omitting the first character), solely to get the character that was removed. It's quite clunky.
In this case, you can just get the first property (from the Collection protocol) on the characters property of String.
Try this:
let demoStrings = ["hi", "there", "world"]
let firstLetters = demoStrings.map {(word: String) -> String in
return word.characters.first
}
or for short:
let firstLetters = demoStrings.map{ $0.characters.first }
Related
I would like to define a unlines function which works which any Sequence whose elements conform to the StringProtocol; this is my attempt:
func unlines<S: StringProtocol>(_ xs: Sequence<S>) -> S {
return xs.joined(separator: "\n")
}
Error: Use of protocol 'Sequence' as a type must be written 'any Sequence'
A version defined for a concrete type does work:
func unlines(_ xs: [String]) -> String {
return xs.joined(separator: "\n")
}
but can only be applied with list of String.
How can I develop a general definition ?
EDIT:
For example, I would like to apply it to the following value:
["Hello", "World"].lazy.map { $0.lowercased() }
joined returns a String so you need to change the return type and use any Sequence
func unlines<S: StringProtocol>(_ xs: any Sequence<S>) -> String {
return xs.joined(separator: "\n")
}
This then works both with String and Substring
String example:
let result = unlines(["A", "B", "C"])
Substring example:
let result = unlines("A-B-C".split(separator: "-"))
both returns
A
B
C
In Swift, you'd typically use a protocol extension to define instance functions that should operate on an instance, rather than using free functions that take a first argument.
Here's how that might work:
extension Sequence where Element: StringProtocol {
// FIXME: This is a pretty Haskelly, non-Swifty name
func unlines() -> String {
joined(separator: "\n")
}
}
let input = ["Hello", "World"]
.lazy
.map { $0.lowercased() }
.unlines()
print(input)
// Prints:
// hello
// world
After reading this article by Tim Ekl - "Swift Tricks: Searching for Objects by Type", I thought "surely there's a generic function there..." so I coded this:
class One {}
let mixedArray:[Any] = ["One", 1, 1.0, One()]
func filterType1<T>(array: [Any]) -> [T] { // Compiles fine, but cannot be called
return array.flatMap( { $0 as? T })
}
//let f1 = filterType1<Int>(array: mixedArray) // syntax error
//let f1 = filterType1(array: mixedArray) // generic parameter 'T' could not be inferred
On reflection, it's obvious that the compiler can't infer the type of T at compile time if I cannot specify the type in the function name, and so to accomplish this, I need to make the seemingly spurious change:
func filterType2<T>(sampleType: T, array: [Any]) -> [T] {
return array.flatMap( { $0 as? T })
}
let f2 = filterType2(sampleType: 2, array: mixedArray)// [1] as required
However, my question is "why does the definition of the function compile, when it is impossible to call?"
Try this:
let f1: [Int] = filterType1(array: mixedArray) // [1]
Another approach:
let f1 = filterType1(array: mixedArray) as [Int]
Please see following code:
let myDict = [Int:Object]()
func task(newId: Int) {
var newObj = myDict[newId]
if (newObj == nil) { // Question (1)
newObj = Object()
myDict[newId] = newObj
newObj!.doSomething() // Question (2)
}
}
Question (1): I am trying to see if an object associated with newId already exits in myDict. If not, create one and assign it in myDict. I am wondering if any better way to do this? It doesn't look very "swift" right now :)
Question (2): I have to add ! here, but I feel kind of odd that I still have to force unwrapping it even I just created a new object one line above. (no Failable Initializer in Object)
Any advise to help me/correct me to know better about Swift is appreciated. Thanks.
var dict: Dictionary<Int,Int> = [1:1,2:2]
let o = dict[3] ?? Int(3)
// now do something with o, it goes from you dict, or it is the 'new' one
// finaly you can update you dict with 'new' o
dict[3] = o
from apple docs
The nil coalescing operator (a ?? b) unwraps an optional a if it
contains a value, or returns a default value b if a is nil. The
expression a is always of an optional type. The expression b must
match the type that is stored inside a.
I'll though I'd add some notes w.r.t. your code that I find important to point out. After these notes, I'll add two quick answers to your question.
First, lets just analyse your code as it is. We note that you have not told us the type of Object, which means that the behaviour of the code is not well-defined, in the sense that it will behave differently depending on if Object is of value- of reference-type.
For this, we'll look a at a full MWE for your case, and here, we'll construct your Object type as a struct type (value type). Now, consider
// and for this example, assume your object is of value-type struct
struct Object {
var someText = "default"
mutating func doSomething() {
someText += "_didSomething"
}
}
// NOTE 1: if you want task(...) to play around with your dictionary, you
// need to pass your dictionary as an argument. In this case, I've used
// inout (see below).
func task(newId: Int, inout myDict: Dictionary<Int,Object>) {
var newObj = myDict[newId]
if (newObj == nil) {
newObj = Object()
myDict[newId] = newObj // this copies newObj by _value_ to
// your dictionary
newObj!.doSomething() // This will not affect the previous
// _copy_ of newObj in your dictionary
}
}
Now, in the code above, you assign newObj to a new entry in your dictionary, but if Object is of value type, this is a value-copy assignment. This means that the following modifications to newObj are performed on a local instance of Object, living only in the scope of the if statement.
As expected, we see that the .doSomething() call on local newObj after value-assignment to your dictionary does not affect the dictionary entry.
var myDict = [Int:Object]()
task(1, myDict: &myDict)
// _inout_: myDict _copied_ to task(), copy modified in task,
// and modified copy again _copied back_ to caller, myDict.
let isThereAStringInHere = myDict[1]?.someText ?? "nope" // -> "default"
// as expected, we get "default" and not "default_didSomething" here
let whatAboutHere = myDict[2]?.someText ?? "nope" // -> "nope"
// as expected, entry <2:Object> does not exist.
Now, the important part here was:
(If you don't know what type you are dealing with, or just to be extra safe) always assume value-type assignments. If Object was of class type, the assignment myDict[newId] = newObj would have been by reference and the subsequent modification newObj!.doSomething() would have applied to the class instance in the dictionary myDict
Ok, after these notes, let's answer your questions.
Question 1
As has been mentioned in previous answer, as well as used in the discussion above, the nil coalescing operator can be used in cases as this. I'll add that, in this context, an if let clause might work just as well. We modify your task(...) function to be:
func taskNew(newId: Int, inout myDict: Dictionary<Int,Object>) {
if let _ = myDict[newId] {
// possibly do something with existing entry
}
else {
// add new entry
var myNewObj = Object()
myNewObj.doSomething()
myDict[newId] = myNewObj
}
}
taskNew(2, myDict: &myDict)
let whatAboutNow = myDict[2]?.someText ?? "nope" // -> "default_didSomething"
The if let clauses are very "Swifty".
If you want to do something with your dictionary entry both for the use where it exist of where you create txt, you could replace the taskNew function above with this condensed one:
func taskNewShort(newId: Int, inout myDict: Dictionary<Int,Object>) {
myDict[newId] = myDict[newId] ?? Object()
myDict[newId]?.doSomething()
}
taskNewShort(3, myDict: &myDict)
let andNow = myDict[3]?.someText ?? "nope" // -> "default_didSomething"
Question 2
The reason for the force unwrapping within your if clause if that your haven´t performed any explicit optional checking, meanwhile defining the myObj as
var newObj = myDict[newId] // <-- typ: Object? (optional Object)
Since newObj is of type optional, you'll have to unwrap at some point. Also, look at the value assignment to newObj in the if clause
newObj = Object() // assigns Object type to Object? type
// -> newObj is still optional (no down casting)
If you look at my answer to Question 1 above, you'll see that newObj is at no point an optional, and lives only in the clause where we know that a new Object instance will be added (copied) to your dictionary.
The full code for this answer follows, to simplify just copying into a playground for tracking what happens. Playgrounds are great for studying these kinds of behaviours.
struct Object {
var someText = "default"
mutating func doSomething() {
someText += "_didSomething"
}
}
func task(newId: Int, inout myDict: Dictionary<Int,Object>) {
var newObj = myDict[newId]
if (newObj == nil) { // Question (1)
newObj = Object()
myDict[newId] = newObj
newObj!.doSomething() // Question (2)
}
}
var myDict = [Int:Object]()
task(1, myDict: &myDict)
let isThereAStringInHere = myDict[1]?.someText ?? "nope" // -> "default"
let whatAboutHere = myDict[2]?.someText ?? "nope" // -> "nope"
func taskNew(newId: Int, inout myDict: Dictionary<Int,Object>) {
if let _ = myDict[newId] {
// possibly do something with existing entry
}
else {
// add new entry
var myNewObj = Object()
myNewObj.doSomething()
myDict[newId] = myNewObj
}
}
taskNew(2, myDict: &myDict)
let whatAboutNow = myDict[2]?.someText ?? "nope" // -> "default_didSomething"
func taskNewShort(newId: Int, inout myDict: Dictionary<Int,Object>) {
myDict[newId] = myDict[newId] ?? Object()
myDict[newId]?.doSomething()
}
taskNewShort(3, myDict: &myDict)
let andNow = myDict[3]?.someText ?? "nope" // -> "default_didSomething"
I would write like this:
var myDict = [Int:Object]()
func task(newId: Int) {
if myDict[newId] == nil {
let newObj = Object()
myDict[newId] = newObj
newObj.doSomething()
}
}
Edit: inside this block you'll have non-null newObj
if let newObj = myDict[newId] {
newObj.doSomethingElse()
}
I'm very new to Swift, but slowly learning by following the Stanford iTunes U course. I have a question about storing and calling functions in an array.
The code I have (below) seems to store the function properly, but when I try to call one of the functions I get this error: '(IntegerLiteralConvertible, IntegerLiteralConvertible) -> $T6' is not identical to (String, Op).
I found this answer that was helpful in getting to where I am, but now I'm stuck.
enum Op {
case binary((Double, Double) -> Double)
}
var ops = [String: Op]()
func addOperation(symbol: String, op: Op) {
ops[symbol] = op
}
addOperation("×", Op.binary(*))
addOperation("÷", Op.binary({ $1 / $0 }))
addOperation("+", Op.binary(+))
addOperation("−", Op.binary({ $1 - $0 }))
var newValue = ops["÷"](6, 3) // Produces the error
My understanding was that ops["÷"] should be the function I stored in the array earlier. Am I not calling it properly?
#Nate Cook answer is corret but why do you have to use enum in this case? Consider using typealias like following :
var ops = [String: Op]()
func addOperation(symbol: String, op:Op) {
ops[symbol] = op
}
addOperation("×", (*))
addOperation("÷", ({ $1 / $0 }))
addOperation("+", (+))
addOperation("−", ({ $1 - $0 }))
var newValue = ops["÷"]?(3,6)
// you have to put this outside of any class
public typealias Op = (Double, Double) -> Double
You have two problems there. First, subscripting a dictionary returns an optional value, so ops["÷"] is an Op? that needs to be unwrapped before you can use it.
Second, the associated value of an enum can't be accessed directly—you can only get the value when pattern matching in a switch statement. I'd suggest adding a computed property to Op that does the work for you:
enum Op {
case binary((Double, Double) -> Double)
var binaryCall: ((Double, Double) -> Double)? {
switch self {
case let .binary(operation):
return operation
}
}
}
Then you would call it like this instead:
var newValue = ops["÷"]?.binaryCall?(6, 3)
// Optional(0.5)
An alternate method of implementation would be to just build an dictionary of binary operations, like so (you still need to unwrap once, though):
typealias BinaryOp = (Double, Double) -> Double
var binaryOps: [String: BinaryOp] = [:]
binaryOps["×"] = { $0 * $1 }
binaryOps["÷"] = { $1 / $0 }
newValue = binaryOps["÷"]?(6, 3)
I am getting a compile time error that myFunc reference is ambiguous.
func f (s: String) -> String { return "version 1: " + s }
func f(sourceString s: String) -> String { return "version 2: " + s }
var myFunc: (String)-> String = f as (sourceString : String)->String
How can I explicitly reference each version of the overloaded function, f, in the example above? If I comment out either declaration of func f it will compile and work. But I would like to know how to reference each of the functions if both are declared. Thanks.
I don't know how to do exactly what you want, but maybe this helps:
var myFunc1: (String)-> String = { s in f(sourceString: s) }
var myFunc2: (String)-> String = { s in f(s) }
You can now call:
let s1 = myFunc1("one") // returns "version 2: one"
let s2 = myFunc2("two") // returns "version 1: two"
Interesting one this. I don’t think it’s possible without doing something along the lines of #marcos’s suggestion. The problem you is you can “cast away” the names in tuples:
let named_pair = (s: "hello", i: 1)
named_pair.s // hello
let anon_pair = named_pair as (String,Int)
// or anon_pair: (String,Int) = named_pair, if you prefer
anon_pair.s // no such member 's'
Now suppose you define two functions, identical except one has named arguments:
func f(s: String, i: Int) { println("_: \(s)") }
func f(#s: String, #i: Int) { println("s: \(s)") }
You can then call it via tuples with named vs unnamed arguments:
f(named_pair) // prints s: hello
f(anon_pair) // prints _: hello
// but if you try to call a named argument function with unnamed tuples:
func g(# s: String, # i: Int) { println("s: \(s)") }
g(anon_pair) // compiler error
let h = g
h(anon_pair) // compiler error
h(named_pair) // works
But because you can cast away these names you can do this:
// compiles and runs just fine...
(g as (String,Int)->())(anon_pair)
let k: (String,Int)->() = g
// as does this
k(anon_pair)
And this ability to do this means it’s not possible to use a type to disambiguate an function overloaded only by argument names, as far as I can tell.
Referencing func f (s: String) -> String { return "version 1: " + s }:
let myFunction = f(s:)
Referencing func f(sourceString s: String) -> String { return "version 2: " + s }:
let myFunction = f(sourceString:)
Referencing func anotherFunction(_ param: Any) {}:
let myFunction = anotherFunction(_:)
If you haven't overloaded the function, you don't need to explicity write out the parameter names when referencing the function.
Number of arguments should vary.
If the number of arguments are same then their data types should
vary.
Example
func f(x : String) -> NSString {
return a
}
func f(x : UInt) -> NSString {
return "{\(x)}"
}
I don't think you can. You can call one or the other:
println(f("test")) // version 1: test
println(f(sourceString: "test")) // version 2: test