Swift Array of Functions - swift

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)

Related

Swift enum custom raw type not expressible by different literals

I was reading through the wonderful blog post by Jon Sundell where he is trying to demonstrate how one can use custom raw values with Swift Enums.
I had a play around with his code and made up a bit of a contrived example to see how robust the Enums with custom raw types are.
I wondered if I can make a type, instance of which can be used as a raw value whilst being expressible by both String and Integer literals. When I implemented ExpressiblebyIntegerLiteral the String part did not work the way I expected and I'm not quite sure why.
Here is the code (the question follows the code):
import Foundation
struct Path: Equatable {
var string: String
var int: Int
}
enum Target: Path {
case content
case resources
case images = "resources/images"
}
extension Path: ExpressibleByIntegerLiteral, ExpressibleByStringLiteral {
init(stringLiteral: String) {
string = stringLiteral
int = 0
}
init(integerLiteral: Int) {
string = ""
int = integerLiteral
}
}
if let stringTarget = Target(rawValue: "content") {
print("stringTarget: \(stringTarget)")
}
if let intTarget = Target(rawValue: 1) {
print("intTarget: \(intTarget)")
}
What I expected the above code to produce is both stringTarget and intTarget being initialized to appropriate Enum cases, however, the stringTarget one turns out to be nil.
If you remove the conformance to ExpressibleByIntegerLiteral protocol (and the appropriate block of code which initializes intTarget), the stringTarget automagically gets initialized as I expected: into Target.string case.
Could someone please explain to me what is going on here?
Solving your Question
What I expected the above code to produce is both stringTarget and intTarget being initialized to appropriate Enum cases, however, the stringTarget one turns out to be nil.
They aren't nil. They are this: ""
This happens because both the .content and .resources cases are not explicitly defined by a String. And because of this, they both take the ExpressibleByIntegerLiteral route, and are hence defined as this ""
init(integerLiteral: Int) {
string = "" // see
int = integerLiteral
}
Solved for Int
Use this fancy method in place of IntValue(rawValue: 1):
func IntValue(_ this: Int) -> String? {
return Target(rawValue: 0) != nil ? String(describing: Target(rawValue: 0)!) : nil
}
Solved for String
First, conform your enum to CaseIterable, like so:
enum Target: Path, CaseIterable {
Next, use this fancy method in place of Target(rawValue: "content"):
func StringValue(_ this: String) -> String? {
return Target.allCases.contains { this == String(describing: $0) } ? this : nil
}
Truly solved for String
Now, I've removed a crucial bug where case foo = "bar" can be found both as 'foo' or 'bar'. If you don't want this, use this code instead:
func StringValue(_ this: String) -> String? {
var found: String? = nil
_ = Target.allCases.filter {
if let a = Target(rawValue: Path.init(string: this, int: 0)) {
found = String(describing: a)
return this == String(describing: a)
}
found = String(describing: $0)
return this == String(describing: $0)
}
return found
}
Custom Raw Values for Enums
Here's a quick tutorial:
I am wondering if enum can conform it's rawValue to the ClosedRange struct, just like it can conform to String.
enum Foo: ClosedRange<Int> {
case bar = 1...4
}
Obviously this is not an option, since 1...4 is not a literal
This seems to work:
enum Foo: ClosedRange<Int> {
case foo = "1...3"
case bar = "1...4"
func overlaps(_ with: Foo) -> Bool { return self.rawValue.overlaps(with.rawValue) }
}
extension ClosedRange: ExpressibleByStringLiteral {
public typealias StringLiteralType = String
public init(stringLiteral value: String) {
let v = value.split(separator: ".")
switch Bound.self {
case is Int.Type: self = (Int(v[0])! as! Bound)...(Int(v[1])! as! Bound)
default: fatalError()
}
}
}
It allows you to do this:
print(Foo.foo.overlaps(Foo.bar))
You can add more types like Double or String using this technique
Side Note: My attempt allows for non-unique rawValues (SR-13212) which is a shame. But I'm not thinking that is fixable:
enum Foo: ClosedRange<Int> {
case foo = "1...3"
case bar = "1...4"
case bar = "1...04" // Duplicate, but Swift allows it.
}

Accessing swift dictionary members using AnyObject

I'm having trouble with understanding how to properly do unwrapping in Swift. Here is the situation:
I have these two classes:
class Alpha
{
var val: Int
init(x: Int)
{
val = x
}
func doSomething() -> Int
{
// does stuff and returns an Int value
return val
}
}
and a second class which is a subclass of the first
class Beta: Alpha
{
var str: [String] = []
override init(x: Int)
{
super.init(x)
}
func doSomething() -> String
{
// does stuff and returns a String
return str
}
}
So far so good, these two classes work as expected. But now I want to create a dictionary to store several of both these class objects. So I've created a class to manage the dictionary and add items to it.
class Gamma
{
var myDict: [String:AnyObject] = [:]
func addAlphaToDict(key: String, val: Int)
{
self.myDict[key] = Alpha(x: val).self
}
func addBetaToDict(key: String, val: Int)
{
self.myDict[key] = Beta(x: val).self
}
}
This also is working, but I think this could somehow be improved. The issue I'm having is now when I try to access values in the dictionary:
var foo = Gamma()
foo.addAlphaToDict(key: "blah", val: 45)
foo.addBetaToDict(key: "bluh", val: 23)
var result1 = (foo.myDict["blah"] as! Alpha).doSomething()
var result2 = (foo.myDict["bluh"] as! Beta).doSomething()
I find that the syntax here is very clunky and I feel like I'm doing it wrong, even though it works. I'm an experienced developer, but have only just started using Swift so I'm not really sure about certain things. Could someone with more Swift experience show me how to improve this code, or point me in the direction I should be going? Thanks!
You can use Alpha instead of AnyObject as your dictionary value in this case. Just downcast it to Beta when needed. Using AnyObject or Any as the dictionary key should be avoided as much as possible.
However, i have to say that this approach is bad. You need to have a clearer logic to decide when the key will be Beta other than just relaying on knowing which key you are passing to the dictionary.
In swift when you want a heterogeneous type with a finite number of possibilities, prefer an enum with associated values to Any or AnyObject. You can then use a switch to recover the type in an exhaustive and type safe way.
import UIKit
import PlaygroundSupport
enum DictionaryValues {
case integer(Int), string(String)
}
let dictionary: [String: DictionaryValues] = ["a": .integer(1), "b": .string("xyz")]
for (key, value) in dictionary {
switch value {
case .integer(let number):
print("Key \(key) is a number: \(number)")
case .string(let text):
print("Key \(key) is text: \(text)")
}
}

Creating an enum instance

suppose I have
enum Example {
case one(string: String)
case two(string: String)
}
and now I have
let x = Example.one(string: "Hello")
The question:
let y = ?
how do I create another instance of the same enum in e, so that I end up with y == .one("World"))
The types of enum cases with associated values are closures with arguments corresponding to the type of the associated values, and with a return corresponding to the type of the enum (with the value of the return being the specific case). I.e., for your example above, the type of Example.one as well as Example.two is (String) -> Example, where the closures expressed by these two cases yield different results; instances of .one(...) and .two(...), respectively.
Hence, instead of writing your own method to "clone" a given case, you could simply have a computed property which returns the already existing closures Example.one and Example.two (if self is one or two, respectively), which can subsequently be invoked upon a String argument to construct a new Example instance (with value .one or .two; along with the supplied associated String value).
E.g.:
enum Example {
case one(string: String) // type: (String) -> Example
case two(string: String) // type: (String) -> Example
var caseClosure: (String) -> Example {
switch self {
case .one: return Example.one
case .two: return Example.two
}
}
}
let x = Example.one(string: "Hello") // .one("Hello")
let y = x.caseClosure("World") // .one("World")
However, since all the cases in your example are closures of the same type, namely (String) -> Example (i.e. have the same number and type(s) of associated values), you might as well, as already proposed in a comment by #Hamish, wrap an enum with no associated values in a struct along with the always-String "associated value" a separate member of the struct. E.g. expanding Hamish's example with some initializers:
struct S {
enum E {
case one
case two
}
var e: E
var string: String // Since "associated value" is always the same type
init(_ e: E, string: String) {
self.e = e
self.string = string
}
init(from s: S, string: String) {
self.e = s.e
self.string = string
}
}
let x = S(.one, string: "Hello")
let y = S(from: x, string: "World")
let z = S(x.e, string: "World")
You do that by calling the Initializer exactly like you did for x:
enum Example {
case one(string: String)
case two(string: String)
}
let x = Example.one(string: "Hello")
print(x) // Prints one("Hello")
let y = Example.one(string: "World")
print(y) // Prints one("World")
Also, The , in your enum declaration is wrong and has to be removed.
UPDATE:
The comment explained the question in more detail, so here is my updated answer:
An elegant way to solve this is to use a function on the original enum type Example.
enum Example {
case one(string: String)
case two(string: String)
func cloneWith(string: String) -> Example {
switch self {
case .one:
return .one(string: string)
case .two:
return .two(string: string)
}
}
}
let x = Example.one(string: "Hello")
print(x) // Prints one("Hello")
let y = x.cloneWith(string: "World")
print(y) // Prints one("World")

How to denote mutable parameters in closures with Swift > 2.2?

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 }

Swift - Assigning Overloaded Function to Variable

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