I am learning some of the fine point of Swift. On function parameters, the documentation says:
Function parameters are constants by default.
… and proceeds to discuss in-out parameters, which I suppose are Swift’s version of reference parameters.
In other languages, parameters behave as local variables, so you can do this with impunity:
func test(a: Int) {
a = a + 1 // cannot assign to a
print(a*2)
}
var x = 3
test(x)
print(x) // -> 3 as before
I know I can easily create local variables, but is there a Swift equivalent to parameters as local variables?
Note:
The SO description for the parameter tag even uses the word “label”:
Parameters are a type of variable used in a subroutine to refer to the data provided as input to the subroutine.
Before Swift 3, Swift used to have them. You used to be able to do something like this:
func f(var x: Int) { // note the "var" modifier here
x += 1
print(x) // prints 2
}
var a = 1
f(a)
print(a) // prints 1
But it was removed in Swift 3, via SE-0003. The Swift community decided that this is not a good feature. The motivations given in that proposal are:
var is often confused with inout in function parameters.
var is often confused to make value types have reference semantics.
Function parameters are not refutable patterns like in if-, while-, guard-, for-in-, and case statements.
Not sure I understood what do you try to achieve, but if you want to modify external x (so it would be not 3 as before), then you have to make function parameter as inout:
func test(_ a: inout Int) {
a = a + 1 // no error anymore
print(a*2)
}
var x = 3
test(&x)
print(x) // -> 4
Update:
func test(_ a: Int) {
var a = a // make read-write
a = a + 1
print(a*2)
}
var x = 3
test(x)
print(x) // -> 3 as before
Recently I have encountered a need of creating an Array of Functions. Unfortunately, Swift language does not provide a top level type for functions, but instead they must be declared by their specific signature (...)->(...). So I tried to write a wrapper that can hold any function and later specialize it to hold only closures having Void return type and any number of arguments.
struct AnyFunction {
let function: Any
init?(_ object: Any){
switch String(describing: object){
case let function where function == "(Function)":
self.function = object
default:
return nil
}
}
func callAsFunction(){
self.function()
}
}
As I was progressing, I have found out that it is not trivial and possibly requires some hacks with introspection, but I failed to find solution, despite of my attempt. The Compiler messaged:
error: cannot call value of non-function type 'Any'
So how would you make this trick that is, to clarify, to define an Object that can hold any functional type?
Clarification:
What I would prefer is defining something like:
typealias SelfSufficientClosure = (...)->Void
var a, b, c = 0
let funcs = FunctionSequence
.init(with: {print("Hi!")}, {a in a + 3}, {b in b + 3}, { a,b in c = a + b})
for f in funcs { f() }
print([a, b, c])
//outputs
//"Hi"
//3, 3, 6
PS This question has relation to this one (Any or a trouble with sequence of functions)
A function is a mapping from inputs to outputs. In your examples, your inputs are void (no inputs), and your outputs are also void (no outputs). So that kind of function is precisely () -> Void.
We can tell that's the right type because of how you call it:
for f in funcs { f() }
You expect f to be a function that takes no inputs and returns no outputs, which is exactly the definition of () -> Void. We can get exactly the input and output you expect by using that type (and cleaning up a few syntax errors):
var a = 0, b = 0, c = 0
let funcs: [() -> Void] = [{print("Hi!")}, {a = a + 3}, { b = b + 3}, { c = a + b}]
for f in funcs { f() }
print(a, b, c, separator: ",")
//outputs
//Hi!
//3, 3, 6
When you write a closure like {a in a + 3}, that doesn't mean "capture a" (which I believe you are expecting). It means "This is a closure that accepts a parameter that will be called a (completely unrelated to the global variable of the same name) and returns that value plus 3." If that's what you meant, then when you called f(), you would need to pass it something and do something with the return value.
You can use an enum to put various functions into the Array and then extract the functions with a switch.
enum MyFuncs {
case Arity0 ( Void -> Void )
case Arity2 ( (Int, String) -> Void)
}
func someFunc(n:Int, S:String) { }
func boringFunc() {}
var funcs = Array<MyFuncs>()
funcs.append(MyFuncs.Arity0(boringFunc))
funcs.append( MyFuncs.Arity2(someFunc))
for f in funcs {
switch f {
case let .Arity0(f):
f() // call the function with no arguments
case let .Arity2(f):
f(2,"fred") // call the function with two args
}
}
Generics
Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.
Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. In fact, you’ve been using generics throughout the Language Guide, even if you didn’t realize it. For example, Swift’s Array and Dictionary types are both generic collections. You can create an array that holds Int values, or an array that holds String values, or indeed an array for any other type that can be created in Swift. Similarly, you can create a dictionary to store values of any specified type, and there are no limitations on what that type can be.
The Problem That Generics Solve
Here’s a standard, nongeneric function called swapTwoInts(::), which swaps two Int values:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
This function makes use of in-out parameters to swap the values of a
and b, as described in In-Out Parameters.
The swapTwoInts(::) function swaps the original value of b into a,
and the original value of a into b. You can call this function to swap
the values in two Int variables:
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3" The
swapTwoInts(::) function is useful, but it can only be used with Int
values. If you want to swap two String values, or two Double values,
you have to write more functions, such as the swapTwoStrings(::) and
swapTwoDoubles(::) functions shown below:
func swapTwoStrings(_ a: inout String, _ b: inout String) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let temporaryA = a
a = b
b = temporaryA
}
My question:
Instead of using generics function we could have just used Any type as the argument type right, that will simple as this
func swapTwoInts(_a:inout Any,_b:inout Int){
let temporaryA = a
a = b
b = temporaryA
}
So how to plays important role in swift?
Can I write code without generics like this
var num = 3
var numtwo = 5
print(num)
print(numtwo)
func swap(_ a: inout Any, _ b: inout Any){
let tempA=a
a=b
b=tempA
}
swap(a: &num, b: &numtwo)
In swift, you can assign value to a variable ONLY if the type of the variable is same with the value. So imagine you have this:
var a = 1
var b = 2
func swapTwoValues(_ a: inout Any, _ b: inout Any) {
let temporaryA = a
a = b
b = temporaryA
}
swapTwoValues(&a, &b) // <- Cannot pass immutable value as inout argument: implicit conversion from 'Int' to 'Any' requires a temporary
So compiler forces you to implicitly assign Any for the type of the variables. This is not what you want I think. So you have to tell the compiler that it doesn't matter the type of the arguments. The only thing matters is they both have same type to fulfill compilers need. So to achieve this, you can use generic or some kind of protocol.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
swapTwoValues(&a, &b) // Done
Tip: You can swap values with tuple in single line of code without using temporaryVar like this: (Even without function and generic and etc.)
(a, b) = (b, a)
I want make a function to swap 2 variables! but for new swift, I can't use 'var' ....
import UIKit
func swapF(inout a:Int, inout with b:Int ) {
print(" x = \(a) and y = \(b)")
(a, b) = (b, a)
print("New x = \(a) and new y = \(b)")
}
swapF(&5, with: &8)
Literals cannot be passed as inout parameters since they are intrinsically immutable.
Use two variables instead:
var i=5
var j=8
swapF(a:&i, with: &j)
Furthermore, with one of the last Swift 3 snapshots, inout should be placed near the type, the prototype of your function becomes:
func swapF(a:inout Int, with b:inout Int )
Is it possible to pass in a tuple into a function as long as their types match up?
When I try it, I get a missing argument in parameter error:
var myTuple = ("Text",10,"More Text")
func myFunction(a:String, b:Int, c:String) {
// etc...
}
myFunction(myTuple)
It was possible, although was deprecated in Swift 2.2:
In Swift 2.1 and earlier it was possible to use a carefully crafted tuple to fill the parameters of a function. So, if you had a function that took two parameters, you could call it with a two-element tuple as long as the tuple had the correct types and element names.
...
This syntax — affectionately called “tuple splat syntax” — is the antithesis of idiomatic Swift’s self-documenting, readable style, and so it’s deprecated in Swift 2.2.
https://swift.org/blog/swift-2-2-new-features/
I came here wanting to know how to pass a tuple as a function parameter. The answers here focus on a different case. I'm not entirely clear what the OP was after.
In any case, here is how to pass a tuple as a parameter. And, for good measure, how to do it variadically.
func acceptTuple(tuple : (Int, String)) {
print("The Int is: \(tuple.0)")
print("The String is '\(tuple.1)'")
}
acceptTuple((45, "zebras"))
// Outputs:
// The Int is: 45
// The String is 'zebras'
func acceptTuples(tuples : (Int, String) ...) {
var index = 0
// note: you can't use the (index, tuple) pattern in the for loop,
// the compiler thinks you're trying to unpack the tuple, hence
/// use of a manual index
for tuple in tuples {
print("[\(index)] - Int is: \(tuple.0)")
print("[\(index)] - String is '\(tuple.1)'")
index++
}
}
acceptTuples((45, "zebras"), (17, "armadillos"), (12, "caterpillars"))
//Outputs
//[0] - Int is: 45
//[0] - String is 'zebras'
//[1] - Int is: 17
//[1] - String is 'armadillos'
//[2] - Int is: 12
//[2] - String is 'caterpillars'
Passing tuples in can be a quick and convenient approach, saving you from having to create wrappers etc. For example, I have a use case where I am passing a set of tokens and parameters to create a game level. Tuples makes this nice and compact:
// function signature
class func makeLevel(target: String, tokens: (TokenType, String)...) -> GameLevel
// The function is in the class Level. TokenType here is an Enum.
// example use:
let level = Level("Zoo Station", tokens:
(.Label, "Zebra"),
(.Bat, "LeftShape"),
(.RayTube, "HighPowered"),
(.Bat, "RightShape"),
(.GravityWell, "4"),
(.Accelerator, "Alpha"))
Yes, it's possible under these conditions:
the tuple must be immutable
the number of values in the tuple, their type, and their order must match the parameters expected by the function
named parameters must match external names in the function signature
non-named parameters must match parameters without external name in the function signature
So, your code is ok, the only thing you have to do is turning the tuple into an immutable one (i.e. using let and not var):
let myTuple = ("Text", 10, "More Text")
func myFunction(a:String, b:Int, c:String) {
// etc...
}
myFunction(myTuple)
One more example with external names:
let myTuple = ("Text", paramB: 10, paramC: "More Text")
func myFunction(a:String, paramB b:Int, paramC c:String) {
// etc...
}
myFunction(myTuple)
In your tuple, it appears as though you must name them and then refer to them as such:
so your code should be
var myTuple = (val1: "Text", val2: 10, val3: "More Text")
func myFunction(a:String, b:Int, c:String) {
// etc...
}
myFunction(myTuple.val1, myTuple.val2, myTuple.val3)
The tuple has named values (val1, val2, val3) which you set and then reference, when you pass in myTuple, to the function myFunction(), it appears as though you are just filling 1 of the 3 available arguements - and with the wrong type to boot! This is the equivalent of storing the types in a tuple, then taking them out for a function call. However, if you want a function to actually take a tuple as a parameter, see below:
var myTuple = (val1: "Text", val2: 10, val3: "More Text")
func tupleFunc(a:(String, Int, String)) {
}
tupleFunc(myTuple)
Yes, but that's the wrong structure: you're passing three variables called a, b, and c rather than a tuple with those components.
You need parentheses around the whole thing:
var myTuple = ("Text", 10, "More Text")
func myFunction(a:(x: String, y: Int, z: String)) {
println(a)
}
myFunction(myTuple)
You can use the following feature: Swift allows you to pass a function (f1) with any number of parameters (but without inout parameters) as a parameter of type (TIn) -> TOut to another function. In this case, TIn will represent a tuple from the parameters of the function f1:
precedencegroup ApplyArgumentPrecedence {
higherThan: BitwiseShiftPrecedence
}
infix operator <- :ApplyArgumentPrecedence
func <-<TIn, TOut>(f: ((TIn) -> TOut), arg: TIn) -> TOut {
return f(arg)
}
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
print(sum <- (40, 2))
In swift 3.0, we should not able to pass the tuple directly to the function.If we did so, it shows the error message as "This type has been removed in swift 3.0"
func sum(x: Int, y: Int) -> Int
return x+y }
let params = (x: 1, y: 1)
let x = params.0
let y = params.1
sum(x: x, y: y)
Hope it helps you!!
The best option for now seems to be to just save it to a compound variable or use the build in dot syntax
let (val1, val2) = (1, 2)
func f(first: Int, second: Int) { }
f(first: val1, second: val2)
let vals = (1, 2)
f(first: vals.0, second: vals.1)
That feature called implicit tuple splat was removed in swift 3.
You can find more detailed explanation on the removal proposal here
Some suggestions to keep using tuple as an argument is by doing so:
func f1(_ a : (Int, Int)) { ... }
let x = (1, 2)
f1(x)
func f2<T>(_ a : T) -> T { ... }
let x = (1, 2)
f2(x)