Passing variadic arguments as an array in Swift - swift

Java 5+ allows passing variadic arguments either as an array, or individually. I expected Swift to allow passing variadic arguments as an array as well, but have no luck so far.
Here is a sample program, I tried in a playground. It creates an array of CVarArg and passes it to String(format:...)
let printData: [(format: String, param: [CVarArg])] =
[(format: "This is blank", param: []),
(format: "This is %# %#", param: ["Swift", "language"])
]
for index in 0...1 {
let pdata = printData[index]
print (String(index+1) + " " + String(format: pdata.format, pdata.param))
}
I expected:
1 This is blank
2 This is Swift language
Instead it prints:
1 This is blank
2 This is (
Swift,
language
) (null)
Tried using CVarArg... as a type:
let printData: [(format: String, param: CVarArg...)]
or bridging to CVarArg... :
String(format: pdata.format, pdata.param as CVarArg...)
In both cases I get compilation errors.
Is there a way to change the syntax of the above code to allow interpreting [CVarArg] in the expected way.

I think your problem is that tuples do not support variadic arguments. So
(format: "This is %# %#", param: ["Swift", "language"])
is a tuple of two things: a string and an array and String(format:,_) sees the array as only one object. To solve your immediate problem, use String(format:,arguments:). e.g.
for index in 0...1 {
let pdata = printData[index]
print (String(index+1) + " " + String(format: pdata.format, arguments: pdata.param))
}
Given how easy it is to create a literal array, I'd question the need to use variadic arguments anywhere in Swift.

Related

Adding numbers inside a string in Swift

Reading through this problem in a book
Given a string that contains both letters and numbers, write a
function that pulls out all the numbers then returns their sum. Sample
input and output
The string “a1b2c3” should return 6 (1 + 2 + 3). The string
“a10b20c30” should return 60 (10 + 20 + 30). The string “h8ers” should
return “8”.
My solution so far is
import Foundation
func sumOfNumbers(in string: String) -> Int {
var numbers = string.filter { $0.isNumber }
var numbersArray = [Int]()
for number in numbers {
numbersArray.append(Int(number)!)
}
return numbersArray.reduce(0, { $0 * $1 })
}
However, I get the error
Solution.swift:8:33: error: cannot convert value of type 'String.Element' (aka 'Character') to expected argument type 'String'
numbersArray.append(Int(number)!)
^
And I'm struggling to get this number of type String.Element into a Character. Any guidance would be appreciated.
The error occurs because Int.init is expecting a String, but the argument number you gave is of type Character.
It is easy to fix the compiler error just by converting the Character to String by doing:
numbersArray.append(Int("\(number)")!)
or just:
numbersArray.append(number.wholeNumberValue!)
However, this does not produce the expected output. First, you are multiplying the numbers together, not adding. Second, you are considering each character separately, and not considering groups of digits as one number.
You can instead implement the function like this:
func sumOfNumbers(in string: String) -> Int {
string.components(separatedBy: CharacterSet(charactersIn: "0"..."9").inverted)
.compactMap(Int.init)
.reduce(0, +)
}
The key thing is to split the string using "non-digits", so that "10" and "20" etc gets treated as individual numbers.

No exact matches in call to subscript , If I use forEach, does not work

Please check the attached code below.
Why Pattern raise Error? , "No exact matches in call to subscript"
What is the difference between A and B?
This is the text import from the console.
3
Everest 8849
K2 8611
Kangchenjunga 8586
This is code
struct Mountain {
let name: String
let height: Int
}
func highestmountain() {
var mtList = [Mountain]()
let N = Int(readLine()!)!
(0..<N)
.forEach { _ in
/* Pattern A */
readLine()!
.split(separator: " ")
.forEach {
mtList.append(Mountain(name: "\($0[0])", height: Int("\($0[1])")!)) // Error: No exact matches in call to subscript
}
/* Pattern B */
let reads = readLine()!
.split(separator: " ")
mtList.append(Mountain(name: "\(reads[0])", height: Int("\(reads[1])")!)) // works!
}
}
In Pattern A, you're using split(" ") which creates an array of Strings (or, more specifically, String.SubSequence), and then you call forEach on that array.
forEach calls the attached closure for each item in the array. So, with your second input line, for example, on the first forEach, $0 will be "Everest" and on the second call, it'll be 8849. However, in your code, you're attempting to get $0[0], but $0 is not an array -- it's a single String.SubSequence. Thus the error about the subscript.
Your second approach (Pattern B) works because reads is an Array of String.SubSequence, so using a subscript (ie the [0]) works.
Unrelated to your question, but it's worth noting that using subscripts like [1] will fail and crash the app if you haven't first checked to make sure that the array has enough items in it. Force unwrapping with a ! (like you do with Int(...)!) can also cause crashes.

swift 5.1 evaluate a closure in the wrong way

I'm having an issue with evaluation of one line of code
if i break it down to two lines, it's working , but in one line of code, it's just evaluate in a 'new' to a 'wrong' way.
my main reason for asking this question, is not to solve it, I know I can use parenthesis to solve it, and break it to Two line, but don't want to solve it, I just want to know why its evaluated like this , and if there's a solution for this : some setting to patch , in Order THAT it will work in ONE LINE OF CODE :
Heres the code that working in Two lines
Heres the code that trying to do the same thing, but rise an error as you can see:
full code of both working and not working :
class ClosuresStack {
var dic = Dictionary<String,(()->String)->String >()
subscript(_ str:String)-> (()->String)->String {
get {
return dic[str]!
}
set {
dic[str] = newValue
}
}
}
func createClosuresStak() -> ClosuresStack {
let cs = ClosuresStack()
func takesAClosureReturnA_string(_ closure:()->String) ->String {
return closure() + " Two"
}
cs["C"] = takesAClosureReturnA_string
return cs
}
let c = createClosuresStak()["C"]
let str = c{"One"}
print(str) // print: One Two
let c = createClosuresStak()["C"]{"One"} // error -->
now, I want to somehow understand how to change it that it will work in ONE LINE OF CODE : meaning that the evaluation of 'createClosuresStak()["C"]{"One"}' will create a closure after ["C"] , and then from that point writing the {"One"}
will make it a full evaluate of the line :
let c = createClosuresStak()["C"]{"One"}
making 'c' a String
if that's not possible, I need to know it Too , tnx :)
UPDATE
tnx for the comments , its help me understand the problem more clearly :
1) im understanding that the createClosuresStak()["C"]{"One"}
acutely trying to add the string 'One' as another parameter to the sub script , and there for the error from the compiler was that is cannot subscript (String,()->String} , 'C' as the string inside the [] , and the other parameter {"One"} -> BUT , isn't that some kind of a bug?, been that i'm using [] ,Cleary the compiler need to 'understand' that I want to subscript a String, also by power of inferring that swift has,
2) now I'm still trying to get that syntax to work as it is so I try to change some things, in order to get it to work :
so I created a function that take a string, and return a dictionary of type : Dictionary<String,()->String>, and then trying so subscript it
and the compiler don't rise an error that way :
func closuresDictionary(_ s:String) -> Dictionary<String,()->String> {
var dic = Dictionary<String,()->String>()
func foo()->String {
return s + " Two"
}
dic["C"] = foo
return dic
}
let c = closuresDictionary("One")["C"]{ "SomeString" }
c is now a closure of type ()->String which does noting with string that I put inside, so the syntax works, but the outcome is not doing anything.
when im changing the return type of the dictionary to a different closure : (String)->String instead of ()->String , im getting the same old error, that I'm trying to subscript a (String,(String)->String)
and I need a function that will take the string inside the {} , and create something from it meaning that I need to subscript to return a closure of (String)->String
its seems like there's no way to do that
im adding two more pictures of my last trying in order to get this line of code in current syntax to work
the wanted syntax working but the outcome is not an outcome not doing any thing with the string inside the {}:
same error, by changing the function to (String)->String
Your example:
let c = createClosuresStak()["C"]{"One"}
is using trailing closure syntax.
Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:
Cannot subscript a value of type 'ClosuresStack' with an argument of type '(String, () -> String)'.
In other words, you can't pass both "C" and the closure {"One"} to the subscripting function.
There are at least 3 ways to fix this and still put it on one line:
Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax
Wrap the closure in () to make the call explicit:
let c1 = createClosuresStak()["C"]({"One"})
print(c1)
Option 2: Wrap the createClosureStak()["C"] in parentheses
That lets Swift know the subscripting only gets "C" as a parameter and allows trailing closure syntax to work as expected:
let c2 = (createClosuresStak()["C"]){"One"}
print(c2)
Option 3: Add .self to the result before the trailing closure syntax:
That again finishes the subscripting call and avoids the confusion.
let c3 = createClosuresStak()["C"].self {"One"}
print(c3)
Personally, I would choose option one, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.

How can I use getopt with command line arguments in Swift 3?

I'm trying to use getopt with command line arguments in Swift 3. I have from Michele Dall'Agata's nice stackoverflow contribution:
let pattern = "abc:"
var buffer = Array( pattern.utf8 ).map { Int8($0) }
When I then use this code:
let option = Int( getopt( CommandLine.argc, CommandLine.arguments, buffer ) )
I get this error:
Cannot convert value of type '[String]' to expected argument type
'UnsafePointer<UnsafeMutablePointer<Int8>?>!'
for CommandLine.arguments, which I am trying to use as argv. Does anyone know the proper syntax for the 2nd argument for getopt? Thanks in advance!
#Hamish already answered the question and explained how to pass CommandLine.unsafeArgv to getopt() in Swift (and why).
Here is a complete self-contained example how a typical getopt
loop can be implemented in Swift 3:
var aFlag = false
var bFlag = false
var cValue: String?
while case let option = getopt(CommandLine.argc, CommandLine.unsafeArgv, "abc:"), option != -1 {
switch UnicodeScalar(CUnsignedChar(option)) {
case "a":
aFlag = true
case "b":
bFlag = true
case "c":
cValue = String(cString: optarg)
default:
fatalError("Unknown option")
}
}
print(aFlag, bFlag, cValue ?? "?")
Remarks:
You can pass a Swift string (here: "abc:") directly to a C
function expecting a (constant) C string, the compiler will automatically
generate a temporary UTF-8 representation.
getopt() return either -1 (if the argument list is exhausted) or an unsigned char converted to an int. Therefore it is safe to
convert the return value to CUnsignedChar (which is UInt8 in Swift).
while is used (abused?) with pattern matching plus an additional
boolean condition to implement the typical C pattern
while ((option = getopt(argc, argv, "abc:")) != -1) { ... }
in Swift.
CommandLine.arguments gives you a friendly Swift [String] of the arguments passed – however you're looking to send the arguments straight back to C. Therefore you can simply use CommandLine.unsafeArgv instead, which will give you the actual raw value of argv passed to your program.
let option = Int( getopt( CommandLine.argc, CommandLine.unsafeArgv, buffer ) )

How to correctly use short Swift closures in calls to ObjC classes

I want to use short form of closure {$0 > 1} in calls to NSIndexSet class:
let indexSet: NSIndexSet = getSomeIndexSet()
let filteredIndexSet = indexSet.indexesPassingTest(){$0 > 1}
but it gives me
Cannot invoke 'indexesPassingTest' with an argument list of type '((_) -> _)'
but this works: indexSet.indexesPassingTest(){(i,s) in i > 1} though type names are still not there.
Is it a bug or am I missing something?
The error message says what you are doing wrong. The argument passed to block are not two different argument rather a single argument which is tuple. So, you will have to access each element in from the tuple.
Based on the comment by Martin R, it seems like closure must match the 2 arguments. So, one can use $0, or $1 or if only one is used then $0 becomes tuple.
let filteredIndexSet = indexSet.indexesPassingTest { $0.0 > 20 }
The $0.0 means the first item in the tuple which is index.