How exactly does a variadic parameter work? - swift

I'm a Swift newbie and am having a bit of trouble understanding what a variadic parameter is exactly, and why it's useful. I'm currently following along with the online Swift 5.3 guide, and this is the example that was given for this type of parameter.
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
Apparently, the variadic parameter called numbers has a type of Double..., which allows it to be used in the body of the function as a constant array. Why does the function return Double(numbers.count) instead of just numbers.count? And instead of creating a variadic parameter, why not just create a parameter that takes in an array that's outside of the function like this?
func addition(numbers : [Int]) -> Int
{
var total : Int = 0
for number in numbers
{
total += number
}
return total
}
let totalBruhs : [Int] = [4, 5, 6, 7, 8, 69]
addition(numbers: totalBruhs)
Also, why can there only be one variadic parameter per function?

Variadic parameters need (well, not need, but nice) to exist in Swift because they exist in C, and many things in Swift bridge to C. In C, creating a quick array of arbitrary length is not so simple as in Swift.
If you were building Swift from scratch with no backwards compatibility to C, then maybe they'd have been added, and maybe not. (Though I'm betting yes, just because so many Swift developers are used to languages where they exist. But then again, languages like Zig have intentionally gotten rid of variadic parameters, so I don't know. Zig also demonstrates that you don't need variadic parameters to bridge to C, but still, it's kind of nice. And #Rob's comments below are worth reading. He's probably not wrong. Also, his answer is insightful.)
But they're also convenient because you don't need to add the [...], which makes it much nicer when there's just one value. In particular, consider something like print:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
Without variadic parameters, you'd need to put [...] in every print call, or you'd need overloads. Variadic doesn't change the world here, but it's kind of nice. It's particularly nice when you think about the ambiguities an overload would create. Say you didn't have variadics and instead had two overloads:
func print(_ items: [Any]) { ... }
func print(_ item: Any) { print([item]) }
That's actually a bit ambiguous, since Array is also a kind of Any. So print([1,2,3]) would print [[1,2,3]]. I'm sure there's some possible work-arounds, but variadics fix that up very nicely.
There can be only one because otherwise there are ambiguous cases.
func f(_ xs: Int..., _ ys: Int...)
What should f(1,2,3) do in this case? What is xs and what is ys?
The function you've shown here doesn't return Double(numbers.count). It converts numbers.count to a Double so it can be divided into another Double. The function returns total / Double(numbers.count).

And instead of creating a variadic parameter, why not just create a parameter that takes in an array that's outside of the function ... ?
I agree with you that it feels intuitive to use arrays for arithmetic functions like “mean”, “sum”, etc.
That having been said, there are situations where the variadic pattern feels quite natural:
There are scenarios where you are writing a function where using an array might not be logical or intuitive at the calling point.
Consider a max function that is supposed to be returning the larger of two values. It doesn’t feel quite right to impose a constraint that the caller must create an array of these values in order to return the larger of two values. You really want to allow a nice, simple syntax:
let result = max(a, b)
But at the same time, as an API developer, there’s also no reason to restrict the max implementation to only allow two parameters. Maybe the caller might want to use three. Or more. As an API developer, we design API’s for naturally calling points for the primary use cases, but provide as much flexibility as we can. So a variadic function parameter is both very natural and very flexible.
There are lots of possible example of this pattern, namely any function that naturally feels like it should take two parameters, but might take more. Consider a union function for two rectangles and you want the bounding rectangle. Again, you don’t want the caller to have to create an array for what might be a simple union of two rectangles.
Another common example would be where you might have a variable number of parameters but might not be dealing with arrays. The classic example would be printf pattern. Or another is where you are interacting with some SQL database and might be binding values to ? placeholders in the SQL or the like (to protect against SQL injection attacks):
let sql = "SELECT book_id, isbn FROM books WHERE title = ? AND author = ?"
let resultSet = db.query(sql, title, author)
Again, in these cases, suggesting that the caller must create an array for this heterogenous collection of values might not feel natural at the calling point.
So, the question isn’t “why would I use variadic parameter where arrays are logical and intuitive?” but rather “why would I force the use of array parameters where it might not be?”

Related

Ambiguous use of 'lazy'

I have no idea why this example is ambiguous. (My apologies for not adding the code here, it's simply too long.)
I have added prefix (_ maxLength) as an overload to LazyDropWhileBidirectionalCollection. subscript(position) is defined on LazyPrefixCollection. Yet, the following code from the above example shouldn't be ambiguous, yet it is:
print([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2)[0]) // Ambiguous use of 'lazy'
It is my understanding that an overload that's higher up in the protocol hierarchy will get used.
According to the compiler it can't choose between two types; namely LazyRandomAccessCollection and LazySequence. (Which doesn't make sense since subscript(position) is not a method of LazySequence.) LazyRandomAccessCollection would be the logical choice here.
If I remove the subscript, it works:
print(Array([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2))) // [0, 1]
What could be the issue?
The trail here is just too complicated and ambiguous. You can see this by dropping elements. In particular, drop the last subscript:
let z = [0, 1, 2].lazy.drop(while: {_ in false}).prefix(2)
In this configuration, the compiler wants to type z as LazyPrefixCollection<LazyDropWhileBidirectionalCollection<[Int]>>. But that isn't indexable by integers. I know it feels like it should be, but it isn't provable by the current compiler. (see below) So your [0] fails. And backtracking isn't powerful enough to get back out of this crazy maze. There are just too many overloads with different return types, and the compiler doesn't know which one you want.
But this particular case is trivially fixed:
print([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2).first!)
That said, I would absolutely avoid pushing the compiler this hard. This is all too clever for Swift today. In particular overloads that return different types are very often a bad idea in Swift. When they're simple, yes, you can get away with it. But when you start layering them on, the compiler doesn't have a strong enough proof engine to resolve it. (That said, if we studied this long enough, I'm betting it actually is ambiguous somehow, but the diagnostic is misleading. That's a very common situation when you get into overly-clever Swift.)
Now that you describe it (in the comments), the reasoning is straightforward.
LazyDropWhileCollection can't have an integer index. Index subscripting is required to be O(1). That's the meaning of the Index subscript versus other subscripts. (The Index subscript must also return the Element type or crash; it can't return an Element?. That's way there's a DictionaryIndex that's separate from Key.)
Since the collection is lazy and has an arbitrary number of missing elements, looking up any particular integer "count" (first, second, etc.) is O(n). It's not possible to know what the 100th element is without walking through at least 100 elements. To be a collection, its O(1) index has to be in a form that can only be created by having previously walked the sequence. It can't be Int.
This is important because when you write code like:
for i in 1...1000 { print(xs[i]) }
you expect that to be on the order of 1000 "steps," but if this collection had an integer index, it would be on the order of 1 million steps. By wrapping the index, they prevent you from writing that code in the first place.
This is especially important in highly generic languages like Swift where layers of general-purpose algorithms can easily cascade an unexpected O(n) operation into completely unworkable performance (by "unworkable" I mean things that you expected to take milliseconds taking minutes or more).
Change the last row to this:
let x = [0, 1, 2]
let lazyX: LazySequence = x.lazy
let lazyX2: LazyRandomAccessCollection = x.lazy
let lazyX3: LazyBidirectionalCollection = x.lazy
let lazyX4: LazyCollection = x.lazy
print(lazyX.drop(while: {_ in false}).prefix(2)[0])
You can notice that the array has 4 different lazy conformations - you will have to be explicit.

Swift syntax: UnsafeMutablePointers in CGPDFDocument.getVersion

Can anyone explain how I'm supposed to use the method 'getVersion' for CGPDFDocument in Swift?
Apple's documentation gives:
func getVersion(majorVersion: UnsafeMutablePointer<Int32>,
minorVersion: UnsafeMutablePointer<Int32>)
"On return, the values of the majorVersion and minorVersion parameters are set to the major and minor version numbers of the document respectively."
So I supply two variables as arguments of the function, and they get filled with the values on exit? Do they need to point to something in particular before the method is called? Why not just type them as integers, if that's what the returned values are?
You use it like this:
var major: Int32 = 0
var minor: Int32 = 0
document.getVersion(majorVersion: &major, minorVersion: &minor)
print("Version: \(major).\(minor)")
The function expects pointers, but if you pass in plain Int32 variables with the & operator, the Swift compiler is smart enough to call the function with pointers to the variables. This is documented in Using Swift with Cocoa and Objective-C: Interacting with C APIs.
The main reason the function works like this is probably that it's a very old C function that has been imported into Swift. C doesn't support tuples as return values; using pointers as in-out parameters is a way to have the function return more than one value. Arguably, it would have been a better design to define a custom struct for the return type so that the function could return the two values in a single type, but the original developers of this function apparently didn't think it was necessary — perhaps unsuprisingly, because this pattern is very common in C.

Swift: Why to functions have parameters and return value types?

I'm filling a few gaps in my existing Swift programming language skills before moving onto the more advanced features.
I have checked Apples... "Swift Programming Language" guide and Google search shows lots of information about the rules around using the parameters, but I am looking for the overall Why , not the How...
Question:
Why is there a need to have parameters and return value types for functions in Swift?
(I am not asking about the parameter names etc., but a higher level (general) question on 'Why')
I have made a number of programs using simple C-Style functions in Swift, without a need for parameters or return values which works fine as I know what the functions should be doing for me.
Simple e.g.
func printName() {
print("John")
}
However, there are 'exceptions' like some in-built Swift functions.
E.g....
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return colors.count
}
etc. for table view or collection view usage .
So the only reason I see for using parameters + return values (type) in functions is to ensure only specific type of data is inputted and outputted.
In other words, its a way to avoid unexpected outcomes. So acting almost like a Switch statement, where if I have a specific condition is met -- a specific output will occur... (in functions... "an action").
Am I correct in this understanding, in trying to understand why / where one needs to use parameters & / or return values types with Functions? Or are there other practical reasons? If so can you provide an example?
Many thanks in advance!!!!
If you don't use parameters, you're tied to use what's visible in your scope.
if your function is inside a ViewController, all properties inside that VC, also global scope variables (horrible)
if your function is in global scope, you can only use global scope symbols (horrible, again)
You really do want to pass in things using parameters. This way you can:
auto check types (compiles is doing for you)
check for optionality
check value ranges
test your functions using Unit Tests. if you use global symbols, you're tied to that symbols, can't change them
less maintenance: changes outside your function doesn't affect you
Also, having a clear return type helps other developers and your future-self to understand what's returning from that function: a tuple? An optional array? A number? Also, having defined output types helps you with testing your functions.
Using only global variables & procedures (chunks of code to process those global vars) could work on a small scale. That's the old approach use with languages like FORTRAN, BASIC, COBOL... The moment your project grows, you need to isolate one part of your code from the other. For that:
use functions & a functional approach
use OOP
Let's imagine a sum function
func sum(a: Int, b:Int) -> Int {
return a + b
}
What happen if we remove the parameter types
func sum(a, b) -> Int {
return a + b // what?
}
Of course this is not allowed by the compiler but let's try to imagine.
Now a and b could be anything (like a String and a UIImage). How could we write the code to sum 2 things and get an Int?
Another test, let's remove the return type
func sum(a:Int, b:Int) -> ??? {
return a + b
}
Now let's try to invoke our crazy function
let tot: Int = sum(1, b: 2)
but since sum could return any kind of value how can we put the result into an Int constant?

How to cast [Int8] to [UInt8] in Swift

I have a buffer that contains just characters
let buffer: [Int8] = ....
Then I need to pass this to a function process that takes [UInt8] as an argument.
func process(buffer: [UInt8]) {
// some code
}
What would be the best way to pass the [Int8] buffer to cast to [Int8]? I know following code would work, but in this case the buffer contains just bunch of characters, and it is unnecessary to use functions like map.
process(buffer.map{ x in UInt8(x) }) // OK
process([UInt8](buffer)) // error
process(buffer as! [UInt8]) // error
I am using Xcode7 b3 Swift2.
I broadly agree with the other answers that you should just stick with map, however, if your array were truly huge, and it really was painful to create a whole second buffer just for converting to the same bit pattern, you could do it like this:
// first, change your process logic to be generic on any kind of container
func process<C: CollectionType where C.Generator.Element == UInt8>(chars: C) {
// just to prove it's working...
print(String(chars.map { UnicodeScalar($0) }))
}
// sample input
let a: [Int8] = [104, 101, 108, 108, 111] // ascii "Hello"
// access the underlying raw buffer as a pointer
a.withUnsafeBufferPointer { buf -> Void in
process(
UnsafeBufferPointer(
// cast the underlying pointer to the type you want
start: UnsafePointer(buf.baseAddress),
count: buf.count))
}
// this prints [h, e, l, l, o]
Note withUnsafeBufferPointer means what it says. It’s unsafe and you can corrupt memory if you get this wrong (be especially careful with the count). It works based on your external knowledge that, for example, if any of the integers are negative then your code doesn't mind them becoming corrupt unsigned integers. You might know that, but the Swift type system can't, so it won't allow it without resort to the unsafe types.
That said, the above code is correct and within the rules and these techniques are justifiable if you need the performance edge. You almost certainly won’t unless you’re dealing with gigantic amounts of data or writing a library that you will call a gazillion times.
It’s also worth noting that there are circumstances where an array is not actually backed by a contiguous buffer (for example if it were cast from an NSArray) in which case calling .withUnsafeBufferPointer will first copy all the elements into a contiguous array. Also, Swift arrays are growable so this copy of underlying elements happens often as the array grows. If performance is absolutely critical, you could consider allocating your own memory using UnsafeMutablePointer and using it fixed-size style using UnsafeBufferPointer.
For a humorous but definitely not within the rules example that you shouldn’t actually use, this will also work:
process(unsafeBitCast(a, [UInt8].self))
It's also worth noting that these solutions are not the same as a.map { UInt8($0) } since the latter will trap at runtime if you pass it a negative integer. If this is a possibility you may need to filter them first.
IMO, the best way to do this would be to stick to the same base type throughout the whole application to avoid the whole need to do casts/coercions. That is, either use Int8 everywhere, or UInt8, but not both.
If you have no choice, e.g. if you use two separate frameworks over which you have no control, and one of them uses Int8 while another uses UInt8, then you should use map if you really want to use Swift. The latter 2 lines from your examples (process([UInt8](buffer)) and
process(buffer as! [UInt8])) look more like C approach to the problem, that is, we don't care that this area in memory is an array on singed integers we will now treat it as if it is unsigneds. Which basically throws whole Swift idea of strong types to the window.
What I would probably try to do is to use lazy sequences. E.g. check if it possible to feed process() method with something like:
let convertedBuffer = lazy(buffer).map() {
UInt8($0)
}
process(convertedBuffer)
This would at least save you from extra memory overhead (as otherwise you would have to keep 2 arrays), and possibly save you some performance (thanks to laziness).
You cannot cast arrays in Swift. It looks like you can, but what's really happening is that you are casting all the elements, one by one. Therefore, you can use cast notation with an array only if the elements can be cast.
Well, you cannot cast between numeric types in Swift. You have to coerce, which is a completely different thing - that is, you must make a new object of a different numeric type, based on the original object. The only way to use an Int8 where a UInt8 is expected is to coerce it: UInt8(x).
So what is true for one Int8 is true for an entire array of Int8. You cannot cast from an array of Int8 to an array of UInt8, any more than you could cast one of them. The only way to end up with an array of UInt8 is to coerce all the elements. That is exactly what your map call does. That is the way to do it; saying it is "unnecessary" is meaningless.

how to pass n argunment to the method

example
add(int a, int b) Here we pass two argument int a, int b,
can we pass n argument with different data types like int,float,long,
is it possible?
I means i need to write only one method which handles n Arguments.
Yes, it is possible to pass arguments with different data types to Objective-C methods. I assume you're speaking about Objective-C, since the question is tagged iphone.
For example, you could have a method like:
- (void)foo:(int)foo bar:(float)bar baz:(long)baz {
// ...
}
If you are talking about C, you could have a function:
void myfunc(int foo, float bar, long baz) {
// ...
}
If you want to write functions that have variable sized argument lists (so it can take 1 parameter, or 2 parameters, or 3, ...), I suggest you take a look at this blog post which discusses variable argument lists in Objective-C/Cocoa or Apple's technical Q&A on variable argument lists.
- (int)add:(int)number1 to:(int)number2 {
return number1 + number2;
}
call it using [obj add:2 to:4]; where obj is the object receiving the add message.
You may use any type of arguments in the above to achieve what you have in mind. For example:
- (void)add:(int)number1 to:(float)number2 {
//print result here
}
Standard type conversions etc would apply like in any programming language.
If you are talking about totally arbitrary number of arguments, take a look at Variadic functions in Objective C (Google it). Plenty of good tutorials in the first search results page.
Anything is possible, but I am pretty certain it would require some serious hacking to get it to work for variable data types. If you can change the requirements to be a variable number of specific data type, then you should look at an excellent tutorial at "Cocoa with Love" : variable-argument-lists-in-cocoa