I need to truncate a text to get a preview. The preview is the text prefix of ~N chars (but not more) and it should not split words in the middle.
preview("aaa", 10) = "aaa"
preview("a b c", 10) = "a b c"
preview("aaa bbb", 5) = "aaa"
preview("a b ccc", 3) = "a b"
I coded a function as follows:
def preview(s:String, n:Int) =
if (s.length <= n) s else s.take(s.lastIndexOf(' ', n))
Would you change or fix it ?
Now I am thinking how to handle the case when the text words are separated by one or more white spaces (including \n,\t, etc.) rather than just a single space. How would you improve the function to handle this case ?
How about the following:
def preview(s: String, n: Int) = if (s.length <= n) {
s
} else {
s.take(s.lastIndexWhere(_.isSpaceChar, n + 1)).trim
}
This function will:
For the strings shorter or equal n return the string (no preview required)
Otherwise find the the last space character in the n + 1 first characters (this will indicate whether the last world is being split, as if it's not than n + 1 will be a space chracter and otherwise a non-space character) and take a string up to this point
Note: The usage of isSpaceChar will not only provide support for space, but also new line or paragraph, which is what I believe you're after (and you can replace it with isWhitespace if you're after even more extended set of word separators).
I propose next one:
-- UPDATED--
def ellipsize(text : String, max : Int): String = {
def ellipsize0(s : String): String =
if(s.length <= max) s
else {
val end = s.lastIndexOf(" ")
if(end == -1) s.take(max)
else ellipsize0(s.take(end))
}
ellipsize0("\\s+".r.replaceAllIn(text, " "))
}
Or your (modified):
def preview(str : String, n : Int) = {
(s : String) => if (s.length <= n) s else s.take(s.lastIndexOf(' ', n))
}.apply( "\\s+".r.replaceAllIn(str, " "))
How about this
def preview(s:String, n:Int) =
if (s.length <= n) s
else s.take(n).takeWhile(_ != ' ')
Try it here: http://scalafiddle.net/console/a05d886123a54de3ca4b0985b718fb9b
This seems to work:
// find the last word that is not split by n, then take to its end
def preview(text: String, n: Int): String =
text take (("""\S+""".r findAllMatchIn text takeWhile (_.end <= n)).toList match {
case Nil => n
case ms => ms.last.end
})
An alternative take (pun intended) but doesn't like input of all whitespace:
text take (("""\S+""".r findAllMatchIn text takeWhile (m => m.start == 0 || m.end <= n)).toList.last.end min n)
Extensionally:
object Previewer {
implicit class `string preview`(val text: String) extends AnyVal {
// find the last word that is not split by n, then take to its end
def preview(n: Int): String =
text take (("""\S+""".r findAllMatchIn text takeWhile (_.end <= n)).toList match {
case Nil => n
case ms => ms.last.end
})
}
}
Looks nice that way:
class PreviewTest {
import Previewer._
#Test def shorter(): Unit = {
assertEquals("aaa", "aaa" preview 10)
}
#Test def spacey(): Unit = {
assertEquals("a b c", "a b c" preview 10)
}
#Test def split(): Unit = {
assertEquals("abc", "abc cba" preview 5)
}
#Test def onspace(): Unit = {
assertEquals("a b", "a b cde" preview 3)
}
#Test def trimming(): Unit = {
assertEquals("a b", "a b cde" preview 5)
}
#Test def none(): Unit = {
assertEquals(" " * 5, " " * 8 preview 5)
}
#Test def prefix(): Unit = {
assertEquals("a" * 5, "a" * 10 preview 5)
}
}
Related
I have a string that contains different ranges and I need to find their value
var str = "some text x = 1..14, y = 2..4 some text"
I used the substringBefore() and substringAfter() methodes to get the x and y but I can't find a way to get the values because the numbers could be one or two digits or even negative numbers.
One approach is to use a regex, e.g.:
val str = "some text x = 1..14, y = 2..4 some text"
val match = Regex("x = (-?\\d+[.][.]-?\\d+).* y = (-?\\d+[.][.]-?\\d+)")
.find(str)
if (match != null)
println("x=${match.groupValues[1]}, y=${match.groupValues[2]}")
// prints: x=1..14, y=2..4
\\d matches a single digit, so \\d+ matches one or more digits; -? matches an optional minus sign; [.] matches a dot; and (…) marks a group that you can then retrieve from the groupValues property. (groupValues[0] is the whole match, so the individual values start from index 1.)
You could easily add extra parens to pull out each number separately, instead of whole ranges.
(You may or may not find this as readable or maintainable as string-manipulation approaches…)
Is this solution fit for you?
val str = "some text x = 1..14, y = 2..4 some text"
val result = str.replace(",", "").split(" ")
var x = ""; var y = ""
for (i in 0..result.count()-1) {
if (result[i] == "x") {
x = result[i+2]
} else if (result[i] == "y") {
y = result[i+2]
}
}
println(x)
println(y)
Using KotlinSpirit library
val rangeParser = object : Grammar<IntRange>() {
private var first: Int = -1
private var last: Int = -1
override val result: IntRange
get() = first..last
override fun defineRule(): Rule<*> {
return int {
first = it
} + ".." + int {
last = it
}
}
}.toRule().compile()
val str = "some text x = 1..14, y = 2..4 some text"
val ranges = rangeParser.findAll(str)
https://github.com/tiksem/KotlinSpirit
I am making a program in SCALA that takes a integer number and reverses it. For example, an input of 30 returns an output of 3. This program must also work for negative numbers, For instance, an input of -89 returns an output of -98. Also, if in the reversal the first digit is 0, it should be truncated (30 to 3). This is the code I have written.
import io.StdIn._
val twoDigitNumber : Int = takeInput()
println("The reversal is " + reverse(twoDigitNumber))
//define a function name reverse to handle the actual reverse process for -ve and +ve numbers
def reverse(x: Integer): Integer = {
//4 possibilities: +ve, 1st digit 0; -ve, 1st digit zero; -ve, 1st digit not zero; +ve, 1st digit not zero
if (x> 0 && x.toString.reverse.charAt(0) == 0) {
x.toString.reverse.substring(1).toInt
} else if (x<0 && x.toString.substring(1).reverse.charAt(0) == 0) {
('-' + x.toString.substring(1).reverse.substring(1)).toInt
} else if (x<0 && x.toString.substring(1).reverse.charAt(0)!= 0) {
('-'+ x.toString.substring(1).reverse).toInt
} else {
x.toString.reverse.toInt
}
}
//reads an integer number
def takeInput() : Int ={
print("Enter a two-digit integer number: ")
readInt()
}
Is there a more efficient way to do this?
The shortest I found:
x.signum * x.abs.toString.reverse.toInt
It can be like below considering x is your integer input:
val reverseOutput = if (x>0) x.toString.reverse.toInt else -1* ((x * -1).toString.reverse.toInt)
def reverseANumber(n: Int): Int = {
def _reverseANumber(i: Int, i1: Int): Int = i match
case 0 => i1
case i =>
val n = i % 10
val n1 = n * math.pow(10, (((math.log10(i) + 1).toInt) - 1)).toInt
_reverseANumber(i / 10, i1 + n1)
_reverseANumber(n, 0)
}
I am currently working on a small code that should allow to tell if a given substring is within a string. I checked all the other similar questions but everybody is using predefined functions. I need to build it from scratch… could you please tell me what I did wrong?
def substring(s: String, t: String): Boolean ={
var i = 0 // position on substring
var j = 0 // position on string
var result = false
var isSim = true
var n = s.length // small string size
var m = t.length // BIG string size
// m must always be bigger than n + j
while (m>n+j && isSim == true){
// j grows with i
// stopping the loop at i<n
while (i<n && isSim == true){
// if characters are similar
if (s(i)==t(j)){
// add 1 to i. So j will increase by one as well
// this will run the loop looking for similarities. If not, exit the loop.
i += 1
j = i+1
// exciting the loop if no similarity is found
}
// answer given if no similarity is found
isSim = false
}
}
// printing the output
isSim
}
substring("moth", "ramathaaaaaaa")
The problem consists of two subproblems of same kind. You have to check whether
there exists a start index j such that
for all i <- 0 until n it holds that substring(i) == string(j + i)
Whenever you have to check whether some predicate holds for some / for all elements of a sequence, it can be quite handy if you can short-circuit and exit early by using the return keyword. Therefore, I'd suggest to eliminate all variables and while-loops, and use a nested method instead:
def substring(s: String, t: String): Boolean ={
val n = s.length // small string size
val m = t.length // BIG string size
def substringStartingAt(startIndex: Int): Boolean = {
for (i <- 0 until n) {
if (s(i) != t(startIndex + i)) return false
}
true
}
for (possibleStartIndex <- 0 to m - n) {
if (substringStartingAt(possibleStartIndex)) return true
}
false
}
The inner method checks whether all s(j + i) == t(i) for a given j. The outer for-loop checks whether there exists a suitable offset j.
Example:
for (
(sub, str) <- List(
("moth", "ramathaaaaaaa"),
("moth", "ramothaaaaaaa"),
("moth", "mothraaaaaaaa"),
("moth", "raaaaaaaamoth"),
("moth", "mmoth"),
("moth", "moth"),
)
) {
println(sub + " " + " " + str + ": " + substring(sub, str))
}
output:
moth ramathaaaaaaa: false
moth ramothaaaaaaa: true
moth mothraaaaaaaa: true
moth raaaaaaaamoth: true
moth mmoth: true
moth moth: true
If you were allowed to use built-in methods, you could of course also write
def substring(s: String, t: String): Boolean = {
val n = s.size
val m = t.size
(0 to m-n).exists(j => (0 until n).forall(i => s(i) == t(j + i)))
}
I offer the following slightly more idiomatic Scala code, not because I think it will perform better than Andrey's code--I don't--but simply because it uses recursion and is, perhaps, slightly easier to read:
/**
* Method to determine if "sub" is a substring of "string".
*
* #param sub the candidate substring.
* #param string the full string.
* #return true if "sub" is a substring of "string".
*/
def substring(sub: String, string: String): Boolean = {
val p = sub.toList
/**
* Tail-recursive method to determine if "p" is a subsequence of "s"
*
* #param s the super-sequence to be tested (part of the original "string").
* #return as follows:
* (1) "p" longer than "s" => false;
* (2) "p" elements match the corresponding "s" elements (starting at the start of "s") => true
* (3) recursively invoke substring on "p" and the tail of "s".
*/
#tailrec def substring(s: Seq[Char]): Boolean = p.length <= s.length && (
s.startsWith(p) || (
s match {
case Nil => false
case _ :: z => substring(z)
}
)
)
p.isEmpty || substring(string.toList)
}
If you object to using the built-in method startsWith then we could use something like:
(p zip s forall (t => t._1==t._2))
But we have to draw the line somewhere between creating everything from scratch and using built-in functions.
I have been trying to implement parallel merge sort in Scala. But with 8 cores, using .sorted is still about twice as fast.
edit:
I rewrote most of the code to minimize object creation. Now it runs about as fast as the .sorted
Input file with 1.2M integers:
1.333580 seconds (my implementation)
1.439293 seconds (.sorted)
How should I parallelize this?
New implementation
object Mergesort extends App
{
//=====================================================================================================================
// UTILITY
implicit object comp extends Ordering[Any] {
def compare(a: Any, b: Any) = {
(a, b) match {
case (a: Int, b: Int) => a compare b
case (a: String, b: String) => a compare b
case _ => 0
}
}
}
//=====================================================================================================================
// MERGESORT
val THRESHOLD = 30
def inssort[A](a: Array[A], left: Int, right: Int): Array[A] = {
for (i <- (left+1) until right) {
var j = i
val item = a(j)
while (j > left && comp.lt(item,a(j-1))) {
a(j) = a(j-1)
j -= 1
}
a(j) = item
}
a
}
def mergesort_merge[A](a: Array[A], temp: Array[A], left: Int, right: Int, mid: Int) : Array[A] = {
var i = left
var j = right
while (i < mid) { temp(i) = a(i); i+=1; }
while (j > mid) { temp(i) = a(j-1); i+=1; j-=1; }
i = left
j = right-1
var k = left
while (k < right) {
if (comp.lt(temp(i), temp(j))) { a(k) = temp(i); i+=1; k+=1; }
else { a(k) = temp(j); j-=1; k+=1; }
}
a
}
def mergesort_split[A](a: Array[A], temp: Array[A], left: Int, right: Int): Array[A] = {
if (right-left == 1) a
if ((right-left) > THRESHOLD) {
val mid = (left+right)/2
mergesort_split(a, temp, left, mid)
mergesort_split(a, temp, mid, right)
mergesort_merge(a, temp, left, right, mid)
}
else
inssort(a, left, right)
}
def mergesort[A: ClassTag](a: Array[A]): Array[A] = {
val temp = new Array[A](a.size)
mergesort_split(a, temp, 0, a.size)
}
Previous implementation
Input file with 1.2M integers:
4.269937 seconds (my implementation)
1.831767 seconds (.sorted)
What sort of tricks there are to make it faster and cleaner?
object Mergesort extends App
{
//=====================================================================================================================
// UTILITY
val StartNano = System.nanoTime
def dbg(msg: String) = println("%05d DBG ".format(((System.nanoTime - StartNano)/1e6).toInt) + msg)
def time[T](work: =>T) = {
val start = System.nanoTime
val res = work
println("%f seconds".format((System.nanoTime - start)/1e9))
res
}
implicit object comp extends Ordering[Any] {
def compare(a: Any, b: Any) = {
(a, b) match {
case (a: Int, b: Int) => a compare b
case (a: String, b: String) => a compare b
case _ => 0
}
}
}
//=====================================================================================================================
// MERGESORT
def merge[A](left: List[A], right: List[A]): Stream[A] = (left, right) match {
case (x :: xs, y :: ys) if comp.lteq(x, y) => x #:: merge(xs, right)
case (x :: xs, y :: ys) => y #:: merge(left, ys)
case _ => if (left.isEmpty) right.toStream else left.toStream
}
def sort[A](input: List[A], length: Int): List[A] = {
if (length < 100) return input.sortWith(comp.lt)
input match {
case Nil | List(_) => input
case _ =>
val middle = length / 2
val (left, right) = input splitAt middle
merge(sort(left, middle), sort(right, middle + length%2)).toList
}
}
def msort[A](input: List[A]): List[A] = sort(input, input.length)
//=====================================================================================================================
// PARALLELIZATION
//val cores = Runtime.getRuntime.availableProcessors
//dbg("Detected %d cores.".format(cores))
//lazy implicit val ec = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(cores))
def futuremerge[A](fa: Future[List[A]], fb: Future[List[A]])(implicit order: Ordering[A], ec: ExecutionContext) =
{
for {
a <- fa
b <- fb
} yield merge(a, b).toList
}
def parallel_msort[A](input: List[A], length: Int)(implicit order: Ordering[A]): Future[List[A]] = {
val middle = length / 2
val (left, right) = input splitAt middle
if(length > 500) {
val fl = parallel_msort(left, middle)
val fr = parallel_msort(right, middle + length%2)
futuremerge(fl, fr)
}
else {
Future(msort(input))
}
}
//=====================================================================================================================
// MAIN
val results = time({
val src = Source.fromFile("in.txt").getLines
val header = src.next.split(" ").toVector
val lines = if (header(0) == "i") src.map(_.toInt).toList else src.toList
val f = parallel_msort(lines, lines.length)
Await.result(f, concurrent.duration.Duration.Inf)
})
println("Sorted as comparison...")
val sorted_src = Source.fromFile(input_folder+"in.txt").getLines
sorted_src.next
time(sorted_src.toList.sorted)
val writer = new PrintWriter("out.txt", "UTF-8")
try writer.print(results.mkString("\n"))
finally writer.close
}
My answer is probably going to be a bit long, but i hope that it will be useful for both you and me.
So, first question is: "how scala is doing sorting for a List?" Let's have a look at the code from scala repo!
def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
val len = this.length
val b = newBuilder
if (len == 1) b ++= this
else if (len > 1) {
b.sizeHint(len)
val arr = new Array[AnyRef](len) // Previously used ArraySeq for more compact but slower code
var i = 0
for (x <- this) {
arr(i) = x.asInstanceOf[AnyRef]
i += 1
}
java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]])
i = 0
while (i < arr.length) {
b += arr(i).asInstanceOf[A]
i += 1
}
}
b.result()
}
So what the hell is going on here? Long story short: with java. Everything else is just size justification and casting. Basically this is the line which defines it:
java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]])
Let's go one level deeper into JDK sources:
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
legacyMergeSort is nothing but single threaded implementation of merge sort algorithm.
The next question is: "what is TimSort.sort and when do we use it?"
To my best knowledge default value for this property is false, which leads us to TimSort.sort algorithm. Description can be found here. Why is it better? Less comparisons that in merge sort according to comments in JDK sources.
Moreover you should be aware that it is all single threaded, so no parallelization here.
Third question, "your code":
You create too many objects. When it comes to performance, mutation (sadly) is your friend.
Premature optimization is the root of all evil -- Donald Knuth. Before making any optimizations (like parallelism), try to implement single threaded version and compare the results.
Use something like JMH to test performance of your code.
You should not probably use Stream class if you want to have the best performance as it does additional caching.
I intentionally did not give you answer like "super-fast merge sort in scala can be found here", but just some tips for you to apply to your code and coding practices.
Hope it will help you.
I miss usable String-functions, that are easy to use, without typing lines of strange identifiers. So I decided to built up a libary with useful and recognicable String-Functions.
I first tried to use Cocoa String-Functions to solve this problem. So I tried in the playground:
import Cocoa
func PartOfString(s: String, start: Int, length: Int) -> String
{
return s.substringFromIndex(advance(s.startIndex, start - 1)).substringToIndex(advance(s.startIndex, length))
}
PartOfString("HelloaouAOUs.World", 1, 5) --> "Hello"
PartOfString("HelloäöüÄÖÜß…World", 1, 5) --> "Hello"
PartOfString("HelloaouAOUs.World", 1, 18) --> "HelloaouAOUs.World"
PartOfString("HelloäöüÄÖÜß…World", 1, 18) --> "HelloäöüÄÖÜß…World"
PartOfString("HelloaouAOUs.World", 6, 7) --> "aouAOUs"
PartOfString("HelloäöüÄÖÜß…World", 6, 7) --> "äöüÄO"
If UnCode Characters are in the String for the case, that "substringFromIndex" is not the Start-Index. And even worse, the Swift-Program crashes sometimes at running time, if UnCode-Characters are in a String, for the case, that "substringFromIndex" is not the Start-Index. So I decided to create a set of new Functions, that take care of this problem and work with UnCode-Characters. Please note, that filenames can contain UnCode-Characters as well. So if you think you do not need UnCode-Characters you are wrong.
If you want to reproduce this, you need the same String I used, because copying from this Web-Page does not reproduce the problem.
var s: String = "HelloäöüÄÖÜß…World"
var t: String = s.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
var u: String = "Helloa%CC%88o%CC%88u%CC%88A%CC%88O%CC%88U%CC%88%C3%9F%E2%80%A6World".stringByRemovingPercentEncoding!
var b: Bool = (s == u) --> true
PartOfString(s, 6, 7) --> "äöüÄO"
Now you could get the idea, to convert the disturbing Canonical-Mapping UniCodes to compatible one with the following function:
func percentescapesremove (s: String) -> String
{
return (s.stringByRemovingPercentEncoding!.precomposedStringWithCompatibilityMapping)
}
And the result you will get is:
var v: String = percentescapesremove(t) --> "HelloäöüÄÖÜß...World"
PartOfString(v, 6, 7) --> "äöüÄÖÜß"
var a: Bool = (s == v) --> false
When you do so, the "äöüÄÖÜß" looks good and you think, everything is OK but look at the "..." which has been permanently converted from UniCode "…" to non-UniCode "..." and has the result which is not identically to the first string. If you have UniCode-filenames, then converting will result in not finding the file on a volume. So it is a good idea to convert only for scree-output and keep the original String in a save place.
The problem with the PartOfString-Function above is, that it generates a new String in the first part of the assignment and uses this new String with the index of the old one, which does not work, because the UniCodes have a different length than the normal letters. So I improved the funktion (thank to Martin R for his help):
func NewPartOfString(s: String, start: Int, length: Int) -> String
{
let t: String = s.substringFromIndex(advance(s.startIndex, start - 1))
return t.substringToIndex(advance(t.startIndex, length))
}
And the result is correct:
NewPartOfString("HelloaouAOUs.World", 1, 5) --> "Hello"
NewPartOfString("HelloäöüÄÖÜß…World", 1, 5) --> "Hello"
NewPartOfString("HelloaouAOUs.World", 1, 18) --> "HelloaouAOUs.World"
NewPartOfString("HelloäöüÄÖÜß…World", 1, 18) --> "HelloäöüÄÖÜß…World"
NewPartOfString("HelloaouAOUs.World", 6, 7) --> "aouAOUs"
NewPartOfString("HelloäöüÄÖÜß…World", 6, 7) --> "äöüÄÖÜß"
In the next step I will show a few functions, that can be used and work well. All of them are based on Integer-Index-Values that will start at 1 for the first character end end with the index for the last character being identically to the length of the String.
This function returns the length of a string:
func len (s: String) -> Int
{
return (countElements(s)) // This works not really fast, because of UniCode
}
This function returns the UniCode-Number of the first UniCode-Character in the String:
func asc (s: String) -> Int
{
if (s == "")
{
return 0
}
else
{
return (Int(s.unicodeScalars[s.unicodeScalars.startIndex].value))
}
}
This function returns the UniCode-Character of the given UniCode-Number:
func char (c: Int) -> String
{
var s: String = String(UnicodeScalar(c))
return (s)
}
This function returns the Upper-Case representation of a String:
func ucase (s: String) -> String
{
return (s.uppercaseString)
}
This function returns the Lower-Case representation of a String:
func lcase (s: String) -> String
{
return (s.lowercaseString)
}
The next Function gives the left part of a String with a given length:
func left (s: String, length: Int) -> String
{
if (length < 1)
{
return ("")
}
else
{
if (length > len(s))
{
return (s)
}
else
{
return (s.substringToIndex(advance(s.startIndex, length)))
}
}
}
The next Function gives the right part of a String with a given length:
func right (s: String, laenge: Int) -> String
{
var L: Int = len(s)
if (L <= laenge)
{
return(s)
}
else
{
if (laenge < 1)
{
return ("")
}
else
{
let t: String = s.substringFromIndex(advance(s.startIndex, L - laenge))
return t.substringToIndex(advance(t.startIndex, laenge))
}
}
}
The next Function gives the part of a String with a given length:
func mid (s: String, start: Int, laenge: Int) -> String
{
if (start <= 1)
{
return (left(s, laenge))
}
else
{
var L: Int = len(s)
if ((start > L) || (laenge < 1))
{
return ("")
}
else
{
if (start + laenge > L)
{
let t: String = s.substringFromIndex(advance(s.startIndex, start - 1))
return t.substringToIndex(advance(t.startIndex, L - start + 1))
}
else
{
let t: String = s.substringFromIndex(advance(s.startIndex, start - 1))
return t.substringToIndex(advance(t.startIndex, laenge))
}
}
}
}
A little more difficult is to get a character at a given position, because we cannot use "substringFromIndex" and "substringToIndex" with "substringFromIndex" is not the Start-Index. So the idea is to trace through the string, character for character, and get the needed substring.
func CharacterOfString(s: String, index: Int, length: Int) -> String
{
var c: String = ""
var i: Int = 0
for UniCodeChar in s.unicodeScalars
{
i = i + 1
if ((i >= index) && (i < index + length))
{
c = c + String(UniCodeChar)
}
}
return (c)
}
But this works not correctly for Strings which contain UniCode-Characters. The following examples show what happens:
CharacterOfString("Swift Example Text aouAOUs.", 16, 8) --> "ext aouA"
len(CharacterOfString("Swift Example Text aouAOUs.", 16, 8)) --> 8
CharacterOfString("Swift Example Text äöüÄÖÜß…", 16, 8) --> "ext äö"
len(CharacterOfString("Swift Example Text äöüÄÖÜß…", 16, 8)) --> 6
So we see, that the resulting String is too short, because a UniCode-Character can contain more than one character. This is because "ä" can be one UniCode-Character and also written as two "a¨" UniCode-Character. So we need another way to get a valid substring.
The solution is, to convert the UniCode-String to an array of UniCode-Characters and to use the index af the array to get a valid character. This works in all cases to get a single Character of an UniCode-String at a given index:
func indchar (s: String, i: Int) -> String
{
if ((i < 1) || (i > len(s)))
{
return ("")
}
else
{
return String(Array(s)[i - 1])
}
}
And with this knowledge, I have built a Function, which can get a valid UniCode-Substring with a given Start-Index and a given length:
func substring(s: String, Start: Int, Length: Int) -> String
{
var L: Int = len(s)
var UniCode = Array(s)
var result: String = ""
var TheEnd: Int = Start + Length - 1
if ((Start < 1) || (Start > L))
{
return ("")
}
else
{
if ((Length < 0) || (TheEnd > L))
{
TheEnd = L
}
for var i: Int = Start; i <= TheEnd; ++i
{
result = result + String(UniCode[i - 1])
}
return (result)
}
}
The next Function searches for the position of a given String in another String:
func position (original: String, search: String, start: Int) -> Int
{
var index = part(original, start).rangeOfString(search)
if (index != nil)
{
var pos: Int = distance(original.startIndex, index!.startIndex)
return (pos + start)
}
else
{
return (0)
}
}
This function looks, if a given Character-Code is a number (0-9):
func number (n: Int) -> Bool
{
return ((n >= 48) & (n <= 57)) // "0" to "9"
}
Now the basic String-Operations are shown, but what about Numbers? How will numbers converted to Strings and vice versa? Let's have a look at converting Strings to Numbers. Please not the "!" in the second line, which is used to get a Int and not an optional Int.
var s: String = "123" --> "123"
var v: Int = s.toInt() --> (123)
var v: Int = s.toInt()! --> 123
But this does not work, if the String contains some characters:
var s: String = "123." --> "123."
var v: Int = s.toInt()! --> Will result in a Runtime Error, because s.toInt() = nil
So I decided to built a smater Function to get the value of a String:
func val (s: String) -> Int
{
var p: Int = 0
var sign: Int = 0
if (indchar(s, 1) == "-")
{
sign = 1
p = 1
}
while(number(asc(indchar(s, p + 1))))
{
p = p + 1
}
if (p > sign)
{
return (left(s, p).toInt()!)
}
else
{
return (0)
}
}
Now the result is correct and does not produce a Runtime-Error:
var s: String = "123." --> "123."
var v: Int = val(s) --> 123
And now the same for Floating-Point Numbers:
func realval (s: String) -> Double
{
var r: Double = 0
var p: Int = 1
var a: Int = asc(indchar(s, p))
if (indchar(s, 1) == "-")
{
p = 2
}
while ((a != 44) && (a != 46) && ((a >= 48) & (a <= 57)))
{
p = p + 1
a = asc(indchar(s, p))
}
if (p >= len(s)) // Integer Number
{
r = Double(val(s))
}
else // Number with fractional part
{
var mantissa: Int = val(substring(s, p + 1, -1))
var fract: Double = 0
while (mantissa != 0)
{
fract = (fract / 10) + (Double(mantissa % 10) / 10)
mantissa = mantissa / 10
p = p + 1
}
r = Double(val(s)) + fract
p = p + 1
}
a = asc(indchar(s, p))
if ((a == 69) || (a == 101)) // Exponent
{
var exp: Int = val(substring(s, p + 1, -1))
if (exp != 0)
{
for var i: Int = 1; i <= abs(exp); ++i
{
if (exp > 0)
{
r = r * 10
}
else
{
r = r / 10
}
}
}
}
return (r)
}
This works for Floating points numbers with exponents:
var s: String = "123.456e3"
var t: String = "123.456e-3"
var v: Double = realval(s) --> 123456
var w: Double = realval(t) --> 0.123456
To generate a String from an Integer is much more simple:
func str (n: Int) -> String
{
return (String(n))
}
A String of a floating point variable does not work with String(n) but can be done with:
func strreal (n: Double) -> String
{
return ("\(n)")
}