I'm reading "The swift programming language 4.2". In the beginning chapter, page 23, I have this following requirement:
Rewrite the closure to return zero for all odd number
And my solution is:
let myArray: [Int] = [1, 2, 3, 4, 5]
myArray.map({ (number: Int) in
if number % 2 != 0 {
return 0
} else {
return number
}
})
But I have this following error:
Ambiguous reference to member 'map'
I really do not understand why I'm wrong, why my 'myArray' can not references to 'map' member?
Could you give me and explanation of this error and the right solution for this?
Thank you!
Make it less ambiguous by specifying the return type with as [Int]:
myArray.map({ (number: Int) in
if number % 2 != 0 {
return 0
} else {
return number
}
}) as [Int]
Or :
let result: [Int] = myArray.map({ (number: Int) in
if number % 2 != 0 {
return 0
} else {
return number
}
})
print(result) //[0, 2, 0, 4, 0]
As noted by vadian: The ambiguity comes from the fact that the generic return type in the closure cannot be inferred.
To understand how the compiler infers the return type of a closure, let's go back to the syntax of a closure :
let myClosure: returnType = { (params) -> returnType in
statements
}
Here, the type of myClosure is returnType. And it's set in two places: after :, and after ->. You could use type inference by removing the returnType from one of the two places, but not both.
So you could remove it from inside the curly braces (like in the code above) :
let result: [Int] = myArray.map({ (number: Int) in
Or just after the variable name, and specifying the return type of the closure inside the the curly braces:
let result = myArray.map({ (number: Int) -> Int in
Rather than annotating the parameter type you have to specify the return type of the closure
let myArray = [1, 2, 3, 4, 5] // You don't even need to annotate the array, the compiler knows the type.
let result = myArray.map({ number -> Int in
if number % 2 != 0 {
return 0
} else {
return number
}
})
Or with trailing closure syntax and shorthand argument names. In this case the compiler can infer everything
let result = myArray.map { $0 % 2 != 0 ? 0 : $0 }
Related
I've read this post, but need a little additional help.
I would like to construct a Closure which takes in a variable amount of Doubles, compares them to a threshold (which is also a Double) and returns a Bool checking if ALL entries were greater than the threshold. The return type should be (Double...) -> Bool
Here is what I have so far:
func isAllAbove(lower: Double) -> (Double...) -> Bool {
return {
var conditions: [Bool] = []
for i in 0...$0.count {
conditions.append(lower < $0[i])
}
return !conditions.contains(false)
}
}
However, the compiler complains that
Cannot convert return expression of type '(_) -> _' to return type '(Double...) -> Bool'
Why is this happening and how can I fix this? Thanks!
Try to specify parameter type and return type in closure to helps compiler to understand what value it should take and return. Also, you have a mistake in for loop. The interval should be like this 0 ..< values.count:
func isAllAbove(lower: Double) -> (Double...) -> Bool {
return { (values: Double...) -> Bool in
var conditions: [Bool] = []
for i in 0 ..< values.count {
conditions.append(lower < values[i])
}
return !conditions.contains(false)
}
}
let allAbove = isAllAbove(lower: 2)
print(allAbove(1, 2, 3)) // false
Also, you can write it almost in 1 line of code:
let lower = 2
let isAllAbove = ![1, 2, 3].contains { $0 < lower }
print(isAllAbove1) // false
I was examining .lazy for high order functions and have got some interesting compile errors related to flatMap function (and possibly others)
Examples
let array = [1, 2, 3, 4, 5, 6]
array
.flatMap {
print("DD")
return $0 // Cannot convert return expression of type 'Int' to return type 'String?'
}
.forEach {
print("SS")
print($0)
}
Commenting out a bit
array
.flatMap {
// print("DD")
return $0
}
.forEach {
print("SS")
print($0)
}
And everything works.. even more interesting example
array
.flatMap {
let z = $0
return $0 // Or return z - all is the same "Cannot convert return expression of type 'Int' to return type 'String?'"
}
.forEach {
print("SS")
print($0)
}
What could cause that behavior?
The flatMap(_:) method on Sequence currently (as of Swift 4) has two different meanings:
It can take a transform closure that returns an optional T?, and it will return a [T], filtering out the nil results (this overload is to be renamed to compactMap(_:) in a future version).
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult]
It can take a transform closure that returns a Sequence, and it will return an array containing the concatenation of all the resulting sequences.
public func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element]
Now, in Swift 4, String became a RangeReplaceableCollection (and therefore a Sequence). So Swift 3 code that did this:
// returns ["foo"], as using the `nil` filtering flatMap, the elements in the closure
// are implicitly promoted to optional strings.
["foo"].flatMap { $0 }
now does this:
// returns ["f", "o", "o"], a [Character], as using the Sequence concatenation flatMap,
// as String is now a Sequence (compiler favours this overload as it avoids the implicit
// conversion from String to String?)
["foo"].flatMap { $0 }
To preserve source compatibility, specialised flatMap overloads were added for strings:
//===----------------------------------------------------------------------===//
// The following overloads of flatMap are carefully crafted to allow the code
// like the following:
// ["hello"].flatMap { $0 }
// return an array of strings without any type context in Swift 3 mode, at the
// same time allowing the following code snippet to compile:
// [0, 1].flatMap { x in
// if String(x) == "foo" { return "bar" } else { return nil }
// }
// Note that the second overload is declared on a more specific protocol.
// See: test/stdlib/StringFlatMap.swift for tests.
extension Sequence {
#_inlineable // FIXME(sil-serialize-all)
#available(swift, obsoleted: 4)
public func flatMap(
_ transform: (Element) throws -> String
) rethrows -> [String] {
return try map(transform)
}
}
extension Collection {
#_inlineable // FIXME(sil-serialize-all)
public func flatMap(
_ transform: (Element) throws -> String?
) rethrows -> [String] {
return try _flatMap(transform)
}
}
Such that the above usage would still return a [String] in Swift 3 compatibility mode, but a [Character] in Swift 4.
So, why does
let array = [1, 2, 3, 4, 5, 6]
array
.flatMap {
print("DD")
return $0 // Cannot convert return expression of type 'Int' to return type 'String?'
}
.forEach {
print("SS")
print($0)
}
tell you that the closure should return a String??
Well, Swift currently doesn't infer parameter and return types for multi-statement closures (see this Q&A for more info). So the flatMap(_:) overloads where the closure returns either a generic T? or a generic S : Sequence aren't eligible to be called without explicit type annotations, as they would require type inference to satisfy the generic placeholders.
Thus, the only overload that is eligible, is the special String source compatibility one, so the compiler is expecting the closure to return a String?.
To fix this, you can explicitly annotate the return type of the closure:
array
.flatMap { i -> Int? in
print("DD")
return i
}
.forEach {
print("SS")
print($0)
}
But if you're not actually using the optional filtering functionality of this flatMap(_:) overload in your real code, you should use map(_:) instead.
flatMap can have different meanings depending on the context. You might tell the compiler more precisely which one you are intending to use.
The two usual purposes to apply flatMap to an array which the compiler can infer is to
flatten nested arrays
let array = [[1, 2, 3, 4, 5, 6], [7, 8, 9]]
let flattened = array.flatMap{$0}
print(flattened) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
map to another type and filter optionals
let array = ["1", "a", "2", "3", "b", "4", "5", "6"]
let flattened = array.flatMap{ Int($0) }
print(flattened) // [1, 2, 3, 4, 5, 6]
Can anyone explain why this single line block with an implicit return compiles:
let r = withUnsafePointer(&msg) {
dn_expand(UnsafePointer($0), eomorig: UnsafePointer($0).advancedBy(msg.count), comp_dn: UnsafePointer($0).advancedBy(offset), exp_dn: &buf, length: buf.count)
}
but this version refactored where the only difference is to avoid the multiple calls to UnsafePointer($0) doesn't:
let s = withUnsafePointer(&msg) {
let p = UnsafePointer($0)
return dn_expand(p, eomorig: p.advancedBy(msg.count), comp_dn: p.advancedBy(offset), exp_dn: &buf, length: buf.count)
}
with error message:
Cannot convert value of type 'inout [UInt8]' (aka 'inout Array<UInt8>') to expected argument type 'inout _'
The dn_function being called is just a trivial wrapper around dn_expand from libresolv:
public static func dn_expand(msg: UnsafePointer<UInt8>, eomorig: UnsafePointer<UInt8>, comp_dn: UnsafePointer<UInt8>, exp_dn: UnsafeMutablePointer<CChar>, length: Int) -> Int {
return Int(res_9_dn_expand(msg, eomorig, comp_dn, exp_dn, Int32(length)))
}
As already said in the comments, withUnsafePointer() is not the
correct way to get a pointer to the element storage. It compiles, but
gives unexpected results, as the following example demonstrates:
var msg: [UInt8] = [1, 2, 3, 4]
func foo(x: UnsafePointer<UInt8>) {
print(x[0])
}
withUnsafePointer(&msg) {
foo(UnsafePointer($0))
}
This prints "random" numbers, but not the expected 1. The correct
way is to call the withUnsafeBufferPointer() method on the array:
msg.withUnsafeBufferPointer {
foo($0.baseAddress)
}
In your case that would be
let r = msg.withUnsafeBufferPointer {
dn_expand($0.baseAddress, eomorig: $0.baseAddress + $0.count, ...)
}
Here the return type of the closure is inferred automatically because
it is a "single-expression" closure. If the closure contains more
than one expression, you have to specify its type:
let r = msg.withUnsafeBufferPointer { bufPtr -> Int in
let p = bufPtr.baseAddress
return dn_expand(p, eomorig: p + msg.count, ...)
}
or let the compiler infer the return type from the context:
let r: Int = msg.withUnsafeBufferPointer { bufPtr in
let p = bufPtr.baseAddress
return dn_expand(p, eomorig: p + msg.count, ...)
}
Swift Tuple index using a variable as the index?
Anyone know if it is possible to use a variable as the index for a Swift tuple index. I wish to select and item from a tuple using a random number. I have the random number as a variable but cannot see how to use that as the index for a tuple. I have searched various places already
Make sure you've chosen the correct data structure
If you've reached a point where you need to access tuple members as if "indexed", you should probably look over your data structure to see if a tuple is really the right choice for you in this case. As MartinR mentions in a comment, perhaps an array would be a more appropriate choice, or, as mentioned by simpleBob, a dictionary.
Technically: yes, tuple members can be accessed by index-style
You can make use of runtime introspection to access the children of a mirror representation of the tuple, and choose a child value given a supplied index.
E.g., for 5-tuples where all elements are of same type:
func getMemberOfFiveTuple<T>(tuple: (T, T, T, T, T), atIndex: Int) -> T? {
let children = Mirror(reflecting: tup).children
guard case let idx = IntMax(atIndex)
where idx < children.count else { return nil }
return children[children.startIndex.advancedBy(idx)].value as? T
}
let tup = (10, 11, 12, 13, 14)
let idx = 3
if let member = getMemberOfFiveTuple(tup, atIndex: idx) {
print(member)
} // 13
Note that the type of child.value is Any, hence we need to perform an attempted type conversion back to the element type of the tuple. In case your tuple contains different-type members, the return type cannot be known at compile time, and you'd probably have to keep using type Any for the returned element.
So, what people here are saying - don't use a tuple if you don't actually need one - is correct. However, there is one caveat.
In my experience, using a Swift Array with a static size in performance critical code actually slows down your code by quite a bit. Take a look:
let clz_lookup = [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
func clz(x : UInt32) -> Int {
guard x != 0 else { return 32 }
var x = x
var n = 0
if (x & 0xFFFF0000) == 0 { n = 16; x <<= 16 } else { n = 0 }
if (x & 0xFF000000) == 0 { n += 8; x <<= 8 }
if (x & 0xF0000000) == 0 { n += 4; x <<= 4 }
return n + clz_lookup[Int(x >> (32 - 4))]
}
This is a fairly straight forward piece of code that uses a cached lookup table at the end. However, if we profile this using instruments we'll see that the lookup is actually much slower than the if statements it replaces! My guess is the reason for that is probably bounds checking or something similar, plus that there is an additional pointer indirection in the implementation.
In any case, that's not good, and since the above is a function that is potentially being called a lot we want to optimise it. A simple technique for that is creating a Tuple instead of an Array, and then simply using withUnsafeMutablePointer to get a pointer directly into the Tuple (which is laid out as a contiguous, static array in memory - exactly what we want in this case!):
var clz_table = (4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
let clz_lookup = withUnsafeMutablePointer(to: &clz_table) { $0.withMemoryRebound(to: Int.self, capacity: 15) { $0 } }
This can be used exactly like a regular C-style array in that you can index into it and even change the values at particular indices, but there is no way to grow this array. This method of indexing should be much faster than the other solutions that are mentioning reflection, but is potentially unsafe if used wrong.
Reference: https://gist.github.com/ctreffs/785db636d68a211b25c989644b13f301
In Swift 5:
func tupleToArray<Tuple, Value>(tuple: Tuple) -> [Value] {
let tupleMirror = Mirror(reflecting: tuple)
assert(tupleMirror.displayStyle == .tuple, "Given argument is no tuple")
assert(tupleMirror.superclassMirror == nil, "Given tuple argument must not have a superclass (is: \(tupleMirror.superclassMirror!)")
assert(!tupleMirror.children.isEmpty, "Given tuple argument has no value elements")
return tupleMirror.children.compactMap { (child: Mirror.Child) -> Value? in
let valueMirror = Mirror(reflecting: child.value)
assert(valueMirror.subjectType == Value.self, "Given tuple argument's child type (\(valueMirror.subjectType)) does not reflect expected return value type (\(Value.self))")
return child.value as? Value
}
}
let sss: [String] = tupleToArray(tuple: ("📷", "🍉🍉", "✈️✈️✈️" ,"🍎🍎🍎"))
print(sss)
You can cast a tuple to a buffer only if it has an homogeneous type.
But before doing so, wonder if an array can't do the job.
var tuple: (Int, Int, Int, Int) = (0,1,2,3)
withUnsafeMutablePointer(to: &tuple) { pointer in
pointer.withMemoryRebound(to: Int.self, capacity: 4) { buffer in
buffer[3] = 0
}
}
print(tuple)
result:
(0, 1, 2, 0)
As long as tuple index is just default name, and you can provide your own names
let testTuple = (first: 1, second: 2.0)
let first: Int = testTuple.first
let second: Double = testTuple.second
you can not use variable as index. Pretty close question "can I use KVC for tuple?", and answer - you can't
I'm trying to do create some closure definitions which I'm gonna use a lot in my iOS app. So I thought to use a typealias as it seemed the most promising ...
I did a small Playground example which shows my issue in detail
// Here are two tries for the Closure I need
typealias AnonymousCheck = (Int) -> Bool
typealias NamedCheck = (number: Int) -> Bool
// This works fine
var var1: AnonymousCheck = {
return $0 > 0
}
var1(-2)
var1(3343)
// This works fine
var var2: NamedCheck = {
return $0 > 0
}
var2(number: -2)
var2(number: 12)
// But I want to use the typealias mainly as function parameter!
// So:
// Use typealias as function parameter
func NamedFunction(closure: NamedCheck) {
closure(number: 3)
}
func AnonymousFunction(closure: AnonymousCheck) {
closure(3)
}
// This works as well
// But why write again the typealias declaration?
AnonymousFunction({(another: Int) -> Bool in return another < 0})
NamedFunction({(another: Int) -> Bool in return another < 0})
// This is what I want... which doesn't work
// ERROR: Use of unresolved identifier 'number'
NamedFunction({NamedCheck in return number < 0})
// Not even these work
// ERROR for both: Anonymous closure arguments cannot be used inside a closure that has exlicit arguments
NamedFunction({NamedCheck in return $0 < 0})
AnonymousFunction({AnonymousCheck in return $0 < 0})
Am I missing something or is it just not supported in Swift?
Thanks
EDIT/ADDITION:
The above is just a simple example. In real life my typealias is more complicated. Something like:
typealias RealLifeClosure = (number: Int, factor: NSDecimalNumber!, key: String, upperCase: Bool) -> NSAttributedString
I basically want to use a typealias as a shortcut so I don't have to type that much. Maybe typealias isn't the right choice... Is there another?
You aren't rewriting the typealias declaration in this code, you're declaring the parameters and return type:
AnonymousFunction({(another: Int) -> Bool in return another < 0})
Happily, Swift's type inference lets you use any of the following - pick the style that feels best to you:
AnonymousFunction( { (number: Int) -> Bool in number < 0 } )
AnonymousFunction { (number: Int) -> Bool in number < 0 }
AnonymousFunction { (number) -> Bool in number < 0 }
AnonymousFunction { number -> Bool in number < 0 }
AnonymousFunction { number in number < 0 }
AnonymousFunction { $0 < 0 }
I don't think you'll be able to do what you want. To simplify your example slightly, you can do this:
typealias NamedCheck = (number: Int) -> Bool
let f: NamedCheck = { $0 < 5 }
f(number: 1)
NamedFunction(f)
NamedFunction( { $0 < 5 } as NamedCheck)
But you can't do what you want, which is to rely on the fact that the tuple arg is called number to refer to it inside the closure without giving it as part of the closure:
// compiler error, no idea what "number" is
let g: NamedCheck = { number < 5 }
Bear in mind that you can name the parameter without giving it a type (which is inferred from the type of g):
let g: NamedCheck = { number in number < 5 }
but also, you can name it whatever you want:
let h: NamedCheck = { whatevs in whatevs < 5 }
NamedFunction(h)
Here's what I think is happening (this is partly guesswork). Remember how functions can have external and internal argument names:
func takesNamedArgument(#namedArg: Int) { etc... }
Or, to write it longhand:
func takesNamedArgument(namedArg namedArg: Int) { etc... }
But you can also give as the second, internal, name whatever you like:
func takesNamedArgument(namedArg whatevs: Int) { etc... }
I think this is what is happening with the closures with named tuples. The "external" name is "number", but you must give it an "internal" name too, which is what you must use in the function body. You can't make use of the external argument within your function. In case of closure expressions, if you don't give an internal name, you can use $0 etc, but you can't just skip it, any more than you can skip the internal name altogether and just rely on the external name when defining a regular function.
I was hoping that I could prove this theory by the following:
let f = { (#a: Int, #b: Int)->Bool in a < b }
resulting in f being of type (a: Int, b: Int)->Bool). This compiles, as does:
let g = { (number1 a: Int, number2 b: Int)->Bool in a < b }
but it doesn't look like the external names for the argument make it out to the type of f or g.
The syntax to create a closure is:
{ (parameters) -> return type in
statements
}
What's at the left of in is the closure signature (parameters and return value). In some cases the signature can be omitted or simplified when type inference is able to determine the number of parameters and their type, and the return value.
In your case it doesn't work because you are passing a type alias, but it is interpreted as a parameter name. The 3 lines work if either you:
name the parameter properly
NamedFunction({number in return number < 0})
AnonymousFunction({number in return number < 0})
use shorthand arguments:
NamedFunction({ return $0 < 0})
AnonymousFunction({ return $0 < 0})
use shorthand arguments and implicit return:
NamedFunction({ $0 < 0})
AnonymousFunction({ $0 < 0})