Hi, is there a method such that if (for example) you want a string of n asterisks, you can say:
myString = “*”.Repeat(n)?
Or do you have to go around a loop? I’m not finding anything…
TIA
Hi, is there a method such that if (for example) you want a string of n asterisks, you can say:
myString = “*”.Repeat(n)?
Or do you have to go around a loop? I’m not finding anything…
TIA
I have included that in my M_String module, based on the code from Joe Strout’s StringUtils.
Var s As String = RepeatStr("*", 3) // “***”
s = s.RepeatStr(2) // “******”
Module String_Utils
Public Function RepeatStr(str As String, numTimes As Integer) As String
If str.Bytes < 1 Or numTimes < 1 Then Return ""
Var alloc() As String
For i As Integer = 1 to numTimes
alloc.Add str
Next
Return String.FromArray(alloc, "")
End Function
Public Function RepeatStr(Extends str As String, numTimes As Integer) As String
Return RepeatStr(str, numTimes)
End Function
End Module
Compare that to the implementation in M_String and StringUtils. You’ll find that it’s much slower.
I won’t, but if someone can get us the timing for a 10000 x=repeat("*", 30), of each, it can be at least interesting.
a method with a MemoryBlock could be fast.
Var m As New MemoryBlock(size)
.. write your char
return m.StringValue(0, m.Size)
I guess it’s worth revisiting now, but back when I was deciding how to write my version of Repeat, I tested various techniques, including arrays and MemoryBlocks, and the StringUtils way was the fastest without question.
MAYBE pumping those bytes in place into the MemoryBlock took some extra nanoseconds and at end just added complexity and no real advantage. So you gave up on it.
Public Function Repeat(z as integer, anz as integer) As string
rem long string create
rem asc(string),size
Var mb As New MemoryBlock(anz)
anz = anz-1
For i As Integer = 0 To anz
mb.Byte(i)=z
Next
Return mb
End Function
10000 views
For x As Integer = 0 To 10000
s = Repeat(s.Asc,num) // Chr() + size
Next
I have 3 system ticks
how is this connected to the requested “repeat a string” ?
NO, you have to implement your own, look at the examples provided or sear the forem, this question has bein answered many times.
You cant write code like that, Xojo does not know that you have a string until you put it on a variable. So, the with the code you chose, you can create a global method that uses EXTENDS to write like that but with a variable
Dim MyString As String = “*”
MyString = MyString.Repeat(x)
The vast changes in the languaje are just aestetics instead of adding functionality.
On average, this memory block based version seems even faster in ARM64 mac:
Public Function RepeatAsBin(Extends value As String, Count As Integer) As String
If count <= 1 Then
Return value
End If
#If Not DebugBuild Then
#Pragma BackgroundTasks False
#Pragma StackOverflowChecking False
#Pragma NilObjectChecking False
#Pragma BoundsChecking False
#EndIf
Var size As Integer = value.Bytes
Var totalSize As Integer = (size * count)
Var mb As New MemoryBlock(totalSize)
mb.StringValue(0, size) = value
Var lastIndex As Integer = (count - 1)
Var index As Integer, offset As Integer
For index = 1 To lastIndex
// Skip the first item as it's the input value
offset = (offset + size)
mb.StringValue(offset, size) = value
Next index
Var result As String = mb
result = result.DefineEncoding(Encodings.UTF8)
Return result
End Function
Alternative pure string based version:
Public Function Repeat(Extends value As String, Count As Integer) As String
If count <= 1 Then
Return value
End If
#If Not DebugBuild Then
#Pragma BackgroundTasks False
#Pragma StackOverflowChecking False
#Pragma NilObjectChecking False
#Pragma BoundsChecking False
#EndIf
Var lastIndex As Integer = (count - 1)
Var values(count) As String = Array(value) // init with first value and count
Var index As Integer
For index = 1 To lastIndex
// Skip the first item as it's the input value
values.AddAt(index, value)
Next index
Var result As String = String.FromArray(values, "")
Return result
End Function
Depends on how you measure the time but i’m interested in knowing which is fastest.
Probably the fastest (and the shortest) method in Xojo:
Public Function repeat_string_fast(string_ as string, times_ as integer) As string
#pragma BoundsChecking false
#pragma StackOverflowChecking false
#pragma NilObjectChecking false
if times_ <= 0 then return ""
if times_ = 1 then return string_
var a() as string
a.ResizeTo(times_)
return string.FromArray(a, string_)
End Function
How does that even makes the string repeat?
Trough the FromArray… ?
It uses the desired string as the joiner for an empty string array with as many places as you wish to repeat. Very clever. Interested in actual speed tests now.
Very clever indeed. Because it passes most of the job to the core native code, probably it’s possibly unbeatable.
I have one little suggestion to shortcut an edge case overhead as repeat("", 99999999):
Public Function repeat_string_fast(string_ as string, times_ as integer) As string
#pragma BoundsChecking false
#pragma StackOverflowChecking false
#pragma NilObjectChecking false
if times_ <= 0 Or string_.Bytes = 0 then return ""
if times_ = 1 then return string_
var a() as string
a.ResizeTo(times_)
return string.FromArray(a, string_)
End Function
Exactly, it is the fastest method because it does nothing except feed string.FromArray() with the appropriate data.
Clever. Did you compare to the one in M_String/StringUtils?
Do you mean the string concatenation?
Honestly, this optmization thing makes only sense if you repeat many times and often. Here are some comparisons in milliseconds repeating a four-byte string 100,000 times:
String = String + String
1,114.35 msString = string.FromArray(<prefilled array with passend string>, "")
21.45 msString = string.FromArray(<resized array with empty items>, String)
2.16 msIf you repeat only three times you need to measure in microseconds to see a difference:
String = String + String
87.19 µsString = string.FromArray(<prefilled array with passend string>, "")
5.48 µsString = string.FromArray(<resized array with empty items>, String)
2.7 µs