Loop works, get error when trying map-reduce - swift

I'm new to map-reduce and wanted to play around with it a bit. I hope this question isn't too stupid.
I have this code working:
var str = "Geometry add to map: "
for element in geometryToAdd {
str.append(element.toString())
}
print(str)
Now I wanted to play around with map-reduce since I learned it recently. I rewrote it as this:
print(geometryToAdd.reduce("Geometry add to map: ", {$0.append($1.toString())}))
This gives me an error error: MyPlayground.playground:127:57: error: type of expression is ambiguous without more context. What do I do wrong?
var geometryToAdd: Array<Geometry> = []
and the class Geometry has a toString function.
Thanks for any help.

There are two similar methods:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
func reduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result
You are using the first version, where $0 is immutable and the closure
must return the accumulated value. This does not compile because
append() modifies its receiver.
Using the second version makes it compile: Here $0 is mutable and
the closure updates $0 with the accumulated value.
print(geometryToAdd.reduce(into: "Geometry add to map: ", {$0.append($1.toString())}))

Make it less ambiguous :
print(geometryToAdd.reduce("Geometry add to map: ", {
$0 + $1.toString()
}))
The error comes from the fact that you can only append() to a variable sequence : $0 is an immutable String. In the loop, str was mutable : a var, not a let.
Have a look at the signature of reduce
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
nextPartialResult is a function/closure that takes two arguments and gives a result. The arguments of this function are immutable, they are not inout parameters. Only inout parameters can be modified.
Find out more on function parameter immutability here :
Function parameters are constants by default. Trying to change the
value of a function parameter from within the body of that function
results in a compile-time error.

You would be better off using joined(separator:) instead of reduce. It has better performance, and lets you put in a separator, if you wish:
print("Geometry add to map: \(geometryToAdd.map(String.init).joined(separator: "")")

Related

What is the Swift compiler doing with my return type? Is it somehow casting?

I have a method:
func allRegions() -> [MappedRegion] {
return self.items.lazy.compactMap { item in item.valveAny }.flatMap { valve in valve.regions }
}
I was frankly surprised this worked. I'm doing lazy stuff here, but it's apparently having a lazy sequence that turns into a sequence of MappedRegion be the same.
Then I was doing some poor mans timing and modified the function to read:
func allRegions() -> [MappedRegion] {
let startTime = Date()
let result = self.items.lazy.compactMap { item in item.valveAny }.flatMap { valve in valve.regions }
self.sumRender += (Date() - startTime)
return result
}
But that created an error:
Cannot convert return expression of type 'LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[StatusRowItem]>.Elements, ValveAbstract?>>, ValveAbstract>.Elements, [MappedRegion]>>>' (aka 'LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<LazyMapSequence<Array<StatusRowItem>, Optional<ValveAbstract>>>, ValveAbstract>, Array<MappedRegion>>>>') to return type '[MappedRegion]'
That was initially a surprise. I found that if I specified the return type of result as [MappedRegion] all was happy (e.g. let result:[MappedRegion] = ...).
What is going on here? I get that the original one line function is inferring the result type as [MappedRegion], so I'm probably not getting much benefits from the lazy use. But what confuses me, is that this coercion from a lazy sequence to a fixed array automagically is reminiscent of casting in C, and I thought that Swift didn't do casting?
No, there is no casting going on. There are simply two different flatMap functions being called. LazyMapSequence has two flatMap(_:) functions (well, technically four, but two are deprecated).
In your first code block, this function is inferred (because this version of flatMap has a return type that matches your allRegions function's return type):
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
And in your second code block, this function is inferred (because there is no type annotation on your local variable that's forcing it to choose the above version of flatMap):
func flatMap<SegmentOfResult>(_ transform: #escaping (Element) -> SegmentOfResult) -> LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<Base, Element>, SegmentOfResult>>> where SegmentOfResult : Sequence

Swift closure syntax

I have following code that work:
let obsScan = source.scan(0, accumulator: +)
let obsReduce = source.reduce(0, accumulator: +)
let obs = Observable.zip(obsScan, obsReduce) { scan, reduce in
return "scan - \(scan), reduce - \(reduce)"
}
I want to rewrite it, using auto complete closure syntax, and i ended up with:
let obs = Observable.zip(obsScan, obsReduce, resultSelector: { (scan, reduce) -> _ in
return "scan - \(scan), reduce - \(reduce)"
})
However, that code throw me multiple errors:
Contextual type for closure argument list expects 2 arguments, which
cannot be implicitly ignored Consecutive statements on a line must be
separated by ';' Expected expression
I can't understand why i use tab to autocomplete function with closure, and when i fill arguments i got an errors?
Whole function declared like that:
public static func zip<O1, O2>(_ source1: O1, _ source2: O2, resultSelector: #escaping (O1.E, O2.E) throws -> RxSwift.Observable.E) -> RxSwift.Observable<RxSwift.Observable.E> where O1 : ObservableType, O2 : ObservableType
I'm not sure what are you waiting for. But this should work:
let obs = Observable.zip(obsScan, obsReduce, resultSelector: { scan, reduce in
return "scan - \(scan), reduce - \(reduce)"
})

Swift 4 filter closure [duplicate]

After the gloss project for Swift 4 in Xcode 9
I am getting following error which i have no idea
Closure tuple parameter '(key: _, value: _)' does not support
destructuring
Code:
extension Dictionary
{
init(elements: [Element]) {
self.init()
for (key, value) in elements {
self[key] = value
}
}
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ (key, value) in
return try transform(key, value)
}))
}
}
Error comes at this point try flatMap({ (key, value)in
Let's start with the definition of flatMap for a dictionary which is the following:
func flatMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
You see that the transform closure takes only one parameter of type Element where Element is just a typealias for a tuple:
public typealias Element = (key: Key, value: Value)
So the first and only argument of the closure should be a tuple of two elements (key of type Key and value of type Value).
Now, if you look at your code (which compiles in Swift 3), you will see that this is not the case, and you should be asking why does this even work in Swift 3.
try flatMap({ (key, value) in
return try transform(key, value)
})
Your closure takes 2 arguments instead of one (key of type Key and value of type Value). This works in Swift 3 thanks to a feature called destructuring where the compiler will automatically transform a tuple of 2 elements into 2 arguments.
But this feature is weird, rarely used and gives unexpected results most of the time so it has been removed in Swift 4.
Edit: As pointed out by OOPer, this feature has been temporarily removed in Swift 4 beta but should be re-added before the final version is out.
Instead you should be writing:
try flatMap({ tupleArgument in
return try transform(tupleArgument.key, tupleArgument.value)
})
And your flatMap function becomes:
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ element in
return try transform(element.key, element.value)
}))
}
It's a side-effect of this proposal for Swift 4:
SE-0110 Distinguish between single-tuple and multiple-argument function types.
But some features included in this proposal caused some regression which is addressed in this post of the evolution-announce mailing list:
[swift-evolution-announce] [Core team] Addressing the SE-0110 usability regression in Swift 4
So, you can expect in the future beta or GM version of Xcode 9, your code would compile well again. Until then, you can use this sort of workaround:
internal func flatMap<KeyPrime , ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime : ValuePrime] {
return Dictionary<KeyPrime,ValuePrime>(elements: try flatMap({ pair in
let (key, value) = pair
return try transform(key, value)
}))
}
By the way, in Swift 4, Dictionary has some new initializers which take Sequence of (Key, Value) pair. For example:
init(uniqueKeysWithValues: S)
I just encountered this error as a result of using enumerated().map():
Closure tuple parameter does not support destructuring
I typed the code:
["foo"].enumerated().map
And then pressed Enter 3 times until Xcode autocompleted the closure boilerplate.
The autocomplete seemingly has a bug that causes the above error. The autocomplete produces double-parenthesis ((offset: Int, element: String)) rather than single-parenthesis (offset: Int, element: String).
I fixed it manually and was able to continue:
// Xcode autocomplete suggests:
let fail = ["foo"].enumerated().map { ((offset: Int, element: String)) -> String in
return "ERROR: Closure tuple parameter does not support destructuring"
}
// Works if you manually replace the "(( _ ))" with "( _ )"
let pass = ["foo"].enumerated().map { (offset: Int, element: String) -> String in
return "works"
}
Possibly the result of using Xcode 10.0 beta (10L176w)
I'm using Xcode 11.1 and Swift 5, and ran into this error while using enumerated().map(). I think this example simplifies things a little, but in general this is what fixed it for me. The true error was the compiler unable to infer to return value:
// Correct Syntax
let resultModels: [ResultModel] = array.enumerated().map { index, model in
// code
}
// Results in the error Closure tuple does not support destructuring
let resultModels = array.enumerated().map { index, model in
// code
}

Why don't shorthand argument names work in this Swift closure?

Here's a Swift function that takes in two ints and a three-arg function, and calls the passed-in function.
func p(x:Int, _ y:Int, _ f: (Int, Int, Int) -> ()) {
f(x, y, 0)
}
I can call this just fine using both trailing closure syntax and shorthand argument names, no problem:
> p(1, 2) {print($0 + $1 + $2)}
3
That worked as expected. But in the Foundation library, there is a string method called enumerateSubstringsInRange defined as follows:
func enumerateSubstringsInRange(
_ range: Range<Index>,
options opts: NSStringEnumerationOptions,
_ body: (substring: String?,
substringRange: Range<Index>,
enclosingRange: Range<Index>,
inout Bool) -> ())
Okay, that's easy enough: the function takes three arguments, the last of which is four-argument function. Just like my first example! Or so I thought....
I can use this function with the trailing closure syntax, but I cannot use shorthand argument names! I have no idea why. This is what I tried:
let s = "a b c"
"a b c".enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {(w,_,_,_) in print(w!)}
a
b
c
All good; I just wanted to print out the matched words, one at a time. That worked when I specified by closure as ``{(w,,,_) in print(w!)}`. HOWEVER, when I try to write the closure with shorthand argument syntax, disaster:
> "a b c".enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {print($0!)}
repl.swift:9:86: error: cannot force unwrap value of non-optional type '(substring: String?, substringRange: Range<Index>, enclosingRange: Range<Index>, inout Bool)' (aka '(substring: Optional<String>, substringRange: Range<String.CharacterView.Index>, enclosingRange: Range<String.CharacterView.Index>, inout Bool)')
So what did I do wrong?! The error message seems to say that closure argument $0 is the whole tuple of args. And indeed, when I tried that, that sure seems to be the case!
>"a b c".enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {print($0.0!)}
a
b
c
So I'm terribly confused. Why in the first case (my function p, are the arguments understood to be $0, $1, etc., but in the second case, all the arguments are rolled up into a tuple? Or are they? FWIW, I found the signature of enumerateSubstringsInRange here.
It depends on the number of parameters.
For example,
func test( closure: (Int,Int,Int) -> Void ){
// do something
}
To make test works as you expect, you must specify $2 ( 3rd argument ). The compiler will infer to the values inside tuple, otherwise it will infer to the tuple itself.
If you don't specify $number that match the number of parameters. For example, only specify $1, will make compile error.
// work as expected ( infer to int )
test{
print($2)
}
test{
print($1+$2)
}
test{
print($0+$1+$2)
}
// not work ( infer to tuple )
test{
print($0)
}
// not work ( cannot infer and compile error )
test{
print($1)
}
There is a question relate to this question. Why is the shorthand argument name $0 returning a tuple of all parameters?

Accessing number of parameters in a closure in Swift

Suppose I have a function:
func test(closure: (closureArgs: Double ...) -> Double){
//some logic
}
Then later, I call it with:
test({$0 + $1 + $2 + $3})
Is it possible to get the number of closureArgs provided within test? The goal would be to do overloading. For example, test could include some code like:
func test(closure: (closureArgs: Double ...) -> Double){
//somehow get access to number of arguments in closureArgs within the closure that was passed.
}
To clarify - I mean I need to access closureArgs's length INSIDE test but OUTSIDE closure
Is it possible to get the number of closureArgs provided within test?
The Short Answer
No.
Slightly Longer Answer
No, it is not. Here's why:
The function is taking a closure as it's argument that by definition takes a variadic number of arguments. There's no possible way for someone calling the function to designate how many arguments the closure should take ahead of time. For example, if I were to call your function, it might look like this:
test() { (closureArgs: Double...) -> Double in
var n: Double = 0
for i in closureArgs {
n += i
}
return n
}
As you'll notice, I don't define the number of arguments anywhere because the number of arguments is only specified when the closure is called. Then the number can be determined inside, or possibly returned.
Basically, the closure is called within test, so only you the caller know how many arguments it takes. Anyone consuming this function has no control over it.
The only way I can think of is to have the closure return a tuple that contains the number of arguments and a function that gives the answer, like this:
func test(c: (Double...) -> (Int, (Double...) -> Double)) {
let (argCount, function): (Int, (Double...) -> Double) = { c($0) }()
switch argCount {
// do something with function here
}
}
func sum(a: Double...) -> (Int, ((Double...) -> Double)) {
return (a.count, { reduce(a, 0, +) })
}
Something like that might work, but I'm not in a place where I can test it.
Edit: I'm trying to test this now, and it's close, but it doesn't work yet...
Edit: Okay, this works, but maybe not in the way that you want...
func test(c: (Int, Double)) {
let (argCount, result) = c
switch argCount {
case 2:
println("2 args were passed")
println("The result was \(result)")
default:
println("nothing")
}
}
func sum(a: Double...) -> (Int, Double) {
return (a.count, reduce(a, 0, +))
}
Now test(sum(2, 4)) will print that the number of arguments was 2 and the result was 6.
The thing is, when you are passing a closure that already has arguments supplied (and it needs arguments to know how to calculate the count) then you're closure function already evaluates to a result. So have the closure return the number of arguments that it used to calculate the result along with the result. Then in test you can work with both the number of arguments and the result.
I don't know...it's hard to know exactly what you are after because I'm not sure what kind of logic you need to implement once you know the number of arguments...
Edit: Sorry I keep going on and on, but it seems like you know how many arguments are going to be included when you call test, right? I mean, in your example you supplied 4 arguments:
test({$0 + $1 + $2 + $3})
(Although, you really need to rewrite that as:
test({ return $0[0] + $0[1] + $0[2] + $0[3] })
...cuz remember, variadic parameters are passed as an array...)
So if you know how many arguments are going to be in the closure when you pass it to test, just include the number of arguments as a parameter in test, like this:
func test(count: Int, closure: (Double...) -> Double) {
switch count {
case 1:
// do what you want if there is only one argument
case 2:
// do what you want if there are two arguments...etc.
}
}
Now when you call test, you just pass the number of arguments as an Int:
test(4, { return $0[0] + $0[1] + $0[2] + $0[3] })
In other words, you always know how many arguments you are passing to the closure when you pass it. If you want test to know, just include that as a parameter.
The answer goes back to how Swift passes arguments to varargs functions, which is as an array. Generally speaking, the function definition:
func takesVarArgs(args: Double...)
Is syntactic sugar, providing for simpler invocation, for the declaration:
func takesVarArgs(args:[Double])
So, in your case, your closure will be invoked as:
func test(closure: (closureArgs:[Double]) -> Double)) -> Double
Then, within your closure you could get the number of arguments with:
closureArgs.count