Split a string into an Array in Swift - string

I'm trying to write my first Swift program, and I know this question has been asked before, but the answers using split aren't working for me. I'm using Xcode 6.4 and Swift 1.2.
I have a String named line.
If I write
let inputs = split(line) {$0 = " "}
as suggested at Swift: Split a String into an array, I get the error message "Cannot invoke 'split' with an argument list of type (String, ()->)"
If I write
let inputs = split(line, {find(" ",$0) != nil}, allowEmptySlices: false)
as suggested at split now complains about missing "isSeparator", I get the error message, "Missing argument for parameter 'isSeparator' in call."
If I jump to the definition of split, I find
func split<S : Sliceable, R : BooleanType>(elements: S, maxSplit: Int = default, allowEmptySlices: Bool = default, #isSeparator: #noescape (S.Generator.Element) -> R) -> [S.SubSlice]
I don't understand what the type of the last parameter is, which is perhaps the root of my problem. Can you tell me how I should call split, and even better can you explain what the parameter type is? Why isn't the type simply (S)->R? I am getting the line from a generator that reads a file line-by-line, if that makes any difference.
for line:String in reader! {
let inputs = split(line) {$0 = " "}
...
}

As said in the comments to the question, the correct way is to use the == operator instead of =.
The type (S.Generator.Element) -> R) must be interpreted in the light of the definition of split:
func split<S : Sliceable, R : BooleanType>
(elements: S,
maxSplit: Int = default,
allowEmptySlices: Bool = default,
#isSeparator: #noescape (S.Generator.Element) -> R)
-> [S.SubSlice]
The type of split is a generic one: in other words, it is a function that can take as first parameter any value that satisfy a generic type (or protocol) subtype of Sliceable, like String, and return a result which must be a subtype of BooleanType (for instance true or false, which are instances of Bool). So the last parameter is a function which gets as parameter a type which is Element of Generator of S (for instance Character) and returns a value of type R. And {$0 == " "} is exactly a predicate of this type, that has an (implicit) parameter ($0), and check if it is equal to the character " ".

Related

Why a type method that should return a string returns a 'unit -> string' instead and how to solve it?

So I have this simple code, I'm trying to figure out classes and inheritance in F#:
type Mkp() =
abstract tX : unit -> string
default this.tX() = ""
type A(n : string, v : string) =
inherit Mkp()
member this.n = n
member this.v = v
override this.tX() = sprintf "%s = \"%s\" " this.n this.v
let test = A "first" "second"
let xxx = "" + test.tX
I get the compiler error: The type 'string' does not match the type 'unit -> string' but I expected test.tX to be a string, what am I doing wrong?
Your class definitions are correct, but there are two small issues when you use them:
When creating the instance, you need to pass parameters to the constructor as a tuple
i.e. you need to say A("one", "two") rather than A "one" "two"
When invoking a method, you need to specify empty parameter list i.e. test.tX()
The correct use of your class A looks like this:
let test = A("first", "second")
let xxx = "" + test.tX()

Is a Swift String constant of a different type than a String literal?

In Swift 2.1 the snippet below generates an error.
var str = "Hello, playground!"
// Success Case
if "!" == str.characters.last {
print("Tone it down please")
}
// Fail Case
let bang = "!"
if bang == str.characters.last { // this line won't compile
print("Tone it down please")
}
The compiler error says:
Binary operator '==' cannot be applied to operands of type 'String'
and '_Element?'
So what is the recommended way to use a constant as opposed to a literal in this situation? (I'm learning Swift, so please feel free to mention if there's a Swift-er way to handle this kind of comparison check.)
Thanks!
For your "Fail case", this is because str.characters.last is an Optional and is a Character, but bang is a String.
You can safely unwrap and compare with if let ... where, and use String() to change the Character to a String for the comparison:
if let last = str.characters.last where String(last) == bang {
print("Tone it down please")
}
As the error says, the first operator is a String, and the second one is an optional Character.
But you've already demonstrated that you know how to turn a string into an Character?, so lets use that:
if bang.characters.last == str.characters.last {
print("Tone it down please")
}
You know that bang.characters.last will just return "!", but now it will be of the same type as str.characters.last, so it will be trivial to compare them.
Thanks for the good discussion. Here's my own answer to improve the illustration by eliminating extraneous optionals, and to demonstrate both the good and the bad of type inference at play:
let a:String = "!" // type is String
let b:Character = "!" // type is Character
let c = "!".characters.last! // type is _Element
let bang = "!" // inferred type is String
if "!" == a { print("literal matches string") }
if "!" == b { print("literal matches Character") }
if "!" == c { print("literal matches _Element") }
if a == b { print("a matches b") } // Err: 'String' to 'Character'
if a == c { print("a matches c") } // Err: 'String' to '_Element'
if b == c { print("b matches c") } // OK: 'Character' to '_Element'
Conclusion: A literal consisting of a single quoted character can be recognized as a String or as a Character (or equivalently an _Element), if the context suggests it.
Importantly: The type of a constant is permanently established when it is declared. The type of a literal is inferred from its context, so the same literal may have different types in different contexts.
Flexible type inference afforded to a literal is not available with a constant.
Not sure if this is totally related, but I found this post as I had problems converting between characters.first, characters.last and Int.
In case this helps anyone:
let element = characters.first! // the ! is important
let myString = String(element)
let myInt = Int(myString) // may be nil if character is not an int

Scala Filter Chars from String

I've got the following code which is supposed to count the numbers of times a character appears in a string.
def filter[T] (l: List[T], stays: T ⇒ Boolean): List[T] = {
if( l == Nil ) return Nil
if (stays(l.head) == true) l.head :: filter(l.tail, stays)
else filter(l.tail, stays)
}
def countChar(s: String): List[(Char, Int)] = {
if (s == "") Nil
else (s(0), s.count(_ == s(0))) :: countChar(filter(s.toList, _ == s(0)).mkString)
}
Now my problem is that in
filter(s.toList, _ == s(0))
I get the error of: missing parameter type. I understand that this comes from nesting the function?
How can I fix this to work? I know that String has some methods to do what I want but I'd like to use my own filter method.
That's a limitation of Scala compiler: it tries to figure out what type T in filter should be, using both arguments l and stays. But it fails because the type of stays argument is unspecified.
If you don't want to specify the type of stays argument every time (i.e., filter(s.toList, (_: Char) == s(0)), you can split filter's argument list into two:
def filter[T] (l: List[T])(stays: T ⇒ Boolean): List[T]
Then Scala will know that T is Char by the time it analyzes the type of stays. You can call this filter with filter(l.tail)(stays).

String method to change particular element in Scala

I need to write a method in Scala that overrides the toString method. I wrote it but I also have to check that if there is an element that is '1' I will change it to 'a', else write the list as it is with the string method. Any suggestions how this can be done?
What error are you getting? seems to work for me
val l = List(1, 2, 3)
println(this)
override def toString(): String = {
val t = l.map({
case 1 => "a"
case x => x
})
t.toString
}
getting List(a, 2, 3) printed out
I see from the comments on your question that list is a List[List[Int]].
Look at the beginning of your code:
list.map { case 1 => 'a'; case x => x}
map expects a function that takes an element of list as a parameter - a List[Int], in your case. But your code works directly on Int.
With this information, it appears that the error you get is entirely correct: you declared a method that expects an Int, but you pass a List[Int] to it, which is indeed a type mismatch.
Try this:
list.map {_.map { case 1 => 'a'; case x => x}}
This way, the function you defined to transform 1 to a and leave everything else alone is applied to list's sublists, and this type-checks: you're applying a function that expects an Int to an Int.

How To Iterate A String In F#?

I've got the following F# code:
//Array iter version
let toSecureString (s:string) =
let sString = new SecureString()
s |> Array.iter (fun cl -> sString.AppendChar cl)
sString
I'm trying to convert a .Net string to a .Net SecureString. When I try to compile I get a Type Mismatch error:
stdin(60,10): error FS0001: Type mismatch. Expecting a
string -> 'a
but given a
'b [] -> unit
The type 'string' does not match the type ''a []'
If I don't specify the type of s, this is the type signature I see:
val toSecureString : char [] -> SecureString
But since I don't want to have to manually create an array of chars for the argument each time, it seems like I am missing something. How can I make this code work with a string parameter being passed in?
If it makes a difference I'm testing on F# 2.0 (Build 4.0.40219.1).
Any hints welcome. If this has already been asked and answered, post a link in the comments and I'll close this question.
Use Seq.iter, not Array.iter, because strings are char seqs but not char[]s.
To manipulate a string as a char seq, one can use String module. This works:
let toSecureString s =
let sString = new SecureString()
String.iter sString.AppendChar s
sString
You can also do this:
SecureString(&&s.ToCharArray().[0], s.Length)

Resources