Compile-time Call Count in Nim - nim-lang

The following code does not compile but illustrates what I would like to do: totalTests should hold the number of time that assertEquals() is called (assertEquals() should probably be a macro for this to be possible, but I'm not familiar with this aspect of Nim yet).
Any idea how this code should be modified to enable the following code to print [1/2] and [2/2] at the beginning of each test report line?
from strutils import format
var countTested = 0
var countFailed = 0
var countPassed = 0
let totalTests = 0 # <-- need let or other compile-time type here (?)
# using proc here... macro may be needed to be able to count calls (?)
proc assertEquals*[T](testName: string, expected: T, p: (proc(): T)) =
countTested += 1
totalTests += 1 # <-- compilation error (how can we increase each time it is called?)
write(stdout, format("[$num/$total] $name: ", "num", countTested, "total", totalTests, "name", testName))
var val = p()
if val == expected:
write(stdout, "passed\n")
countPassed += 1
else:
write(stdout, "failed\n")
countFailed += 1
when isMainModule:
assertEquals("test #A", 12, proc(): int = 14-2)
assertEquals("test #B", 12, proc(): int = 12-2)
Edit: added questions in code

Here is one way to do it. You can execute code at compile time by using a macro or a static statement. Note that there's still no way to reliably count these across multiple modules.
import macros, strutils
proc beginTests()
var countTested = 0
var countFailed = 0
var countPassed = 0
var totalTests = 0
var totalTestsCT {.compiletime.} = 0
macro endTests(): stmt =
quote do:
proc beginTests() =
totalTests = `totalTestsCT`
proc assertEqualsImpl*[T](testName: string, expected: T, p: (proc(): T)) =
countTested += 1
write(stdout, format("[$num/$total] $name: ",
"num", countTested, "total", totalTests, "name", testName))
var val = p()
if val == expected:
write(stdout, "passed\n")
countPassed += 1
else:
write(stdout, "failed\n")
countFailed += 1
macro assertEquals*[T](testName: string, expected: T, p: (proc(): T)): stmt =
totalTestsCT += 1
quote do:
assertEqualsImpl(`testName`, `expected`, `p`)
when isMainModule:
beginTests()
assertEquals("test #A", 12, proc(): int = 14-2)
assertEquals("test #B", 12, proc(): int = 12-2)
endTests()
An alternative implementation would be to embed the tests in a custom block statement, e.g.
testSuite:
assertEquals("test #A", 12, proc(): int = 14-2)
assertEquals("test #B", 12, proc(): int = 12-2)
The testSuite macro would then count the assertions in the embedded code and initialize the variable accordingly.
Yet another solution would be to not execute the tests directly, but store them in a list and only execute them at the end.

Here is an implementation of Reimer's third suggestion, which worked best for me.
import macros, strutils
type
TestSuiteObj = object
countTested: int
countFailed: int
countPassed: int
totalTests: int
tests: seq[(proc (self: TestSuite))]
TestSuite* = ref TestSuiteObj
proc newTestSuite*(): TestSuite =
new(result)
result.countTested = 0
result.countFailed = 0
result.countPassed = 0
result.totalTests = 0
result.tests = #[]
proc assertEquals*[T](self: TestSuite, testName: string, expected: T, p: (proc(): T)) =
self.totalTests += 1
var testProc = proc(self: TestSuite) =
self.countTested += 1
write(stdout, format("[$num/$total] $name: ", "num", self.countTested, "total", self.totalTests, "name", testName))
var val = p()
if val == expected:
write(stdout, "passed\n")
self.countPassed += 1
else:
write(stdout, "failed\n")
self.countFailed += 1
self.tests.add(testProc)
proc run*(self: TestSuite) =
self.totalTests = self.tests.len
for p in self.tests:
p(self)
var verdict = case (self.countTested == self.countPassed)
of true: "PASSED"
of false: "FAILED"
echo format("$verdict. Passed [$passed/$total] tests.", "verdict", verdict, "passed", self.countPassed, "total", self.countTested)
# Sanity
assert(self.countTested == (self.countFailed+self.countPassed))
assert(self.countTested == self.totalTests)
when isMainModule:
var suite = newTestSuite()
suite.assertEquals("test #A", 12, proc(): int = 14-2)
suite.assertEquals("test #B", 12, proc(): int = 12-2)
suite.run()

Related

Have to count a character occurrence in Nim string type

How to count a character occurrence in string in Nim, mainly using its native statements prior go to module ? eg.
var
str = "Hello World"
c : int
c = numChar( "o", str ) # <- illustration only ?
The earlier answer is correct but if you do not want to import any modules you can write your own procedure:
proc count_char(value: string = "Hello World", ch: char = 'o'): int =
var cnt_c: int = 0
for c in value:
if c == ch:
cnt_c += 1
result = cnt_c
var
val: string = "Mother Goose"
ch: char = 'o'
echo $count_char(val, ch)
PS: Unrelated - Need syntax highlight for nim-lang on SO.
Use the count function from strutils:
import std/strutils
let str = "Hello World"
let count = count(str, 'o')
assert count = 1
There’s also a string overload for counting sub strings as well.

Swift generate random number without arc4random [duplicate]

Im having 2 problems when trying to generate a random string in Linux with Swift 3.
arc4random_uniform is not available in Linux only on BSD. SO i was able to get away with using random() function. And this worked when i was generating random numbers of a variable size (See code below)
func generateRandomNumber() -> Int
{
var place = 1
var finalNumber = 0;
#if os(Linux)
for _ in 0..<5
{
place *= 10
let randomNumber = Int(random() % 10) + 1
finalNumber += randomNumber * place
}
#else
for _ in 0..<5
{
place *= 10
let randomNumber = Int(arc4random_uniform(10))
finalNumber += randomNumber * place
}
#endif
return finalNumber
}
And that WORKS.
Edit: it works but it gives me the same number every time :(
When trying to generate random alphanumeric string I'm limited to using Swift String and NOT NSSTRING. Linux throws this error
original pre Linux block of code:
func randomString(_ length: Int) -> String
{
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
And the actual error I get when using above code
error: cannot convert value of type 'NSString' to type 'String' in coercion
randomString += NSString(characters: &nextChar, length: 1) as String
modified for linux block of code.
func randomString(_ length: Int) -> String
{
let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = letters.characters.count
var randomString = ""
#if os(Linux)
for _ in 0..<length
{
let randomValue = (random() % len) + 1
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(randomValue))])"
}
#else
for _ in 0 ..< length
{
let rand = arc4random_uniform(UInt32(len))
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(rand))])"
}
#endif
return randomString
}
but this time the error is weird it only says Illegal instruction with no extra information. I ran the docker container in interactive mode and i saw my server running and printing out when calling other functions etc.
but the thing is the function actually WORKS when i ran it in IBMs swift
sandbox
and I'm assuming its using linux also. Im very stuck and confused any help would be greatly appreciated.
(UPDATE): I ran the same function in just a linux env with a single swift file and not the Vapor swift web framework. and it works. As mentioned in my edit above it gives me the same random string everytime. I will still have to test the entire project once my build finishes. But besides that i need to know if the random() function will actually give me something new each time instead of the same crap.
Figured it out.
So the answer to the repeating random number/string was to just add this line before i called the random() function
srand(UInt32(time(nil)))
and I'm assuming thats what fixed the illegal instruction also. Because i don't recall changing anything else.
Needless to say here is my final result
func generateRandomNumber() -> Int
{
var place = 1
var finalNumber = 0;
#if os(Linux)
srand(UInt32(time(nil)))
for _ in 0..<5
{
place *= 10
let randomNumber = Int(random() % 10) + 1
finalNumber += randomNumber * place
}
#else
for _ in 0..<5
{
place *= 10
let randomNumber = Int(arc4random_uniform(10))
finalNumber += randomNumber * place
}
#endif
return finalNumber
}
func randomString(_ length: Int) -> String
{
let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = letters.characters.count
var randomString = ""
#if os(Linux)
srand(UInt32(time(nil)))
for _ in 0..<length
{
let randomValue = (random() % len) + 1
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(randomValue))])"
}
#else
for _ in 0 ..< length
{
let rand = arc4random_uniform(UInt32(len))
randomString += "\(letters[letters.index(letters.startIndex, offsetBy: Int(rand))])"
}
#endif
return randomString
}
1) Always the same number
You have to set a seed once to get "random" numbers from random():
randomSeed(Int(Date().timeIntervalSince1970)
Man page:
If no seed value is provided, the random() function is
automatically seeded with a value of 1.
As the seed is always the same (1), you always get the same sequence of "random" numbers.
2) Alphanumeric string
To create your string without using NSString:
func randomString(length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.characters.count)
var randomString = ""
for _ in 0 ..< length {
let rand = myCustomRandom(len)
let randIndex = letters.index(letters.startIndex, offsetBy: Int(rand))
let nextChar = letters[randIndex]
randomString += String(nextChar)
}
return randomString
}
I copied and pasted your code exactly, and it doesn't compile.
fatal error: Can't form a Character from an empty String
Here's an alternative method:
// Keep at top of your code (outside of functions)
#if os(Linux)
srandom(UInt32(time(nil)))
#endif
func getRandomNumber(_ min: Int, _ max: Int) -> Int {
#if os(Linux)
return Int(random() % max) + min
#else
return Int(arc4random_uniform(UInt32(max)) + UInt32(min))
#endif
}
func getRandomString(_ chars: String, _ length: Int) -> String {
var str = ""
for _ in 1...length {
str.append(chars.itemOnStartIndex(advancedBy: getRandomNumber(0, chars.count - 1)))
}
return str
}
// Best practice to define this outside of the function itself
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
print(getRandomString(chars, 10))
This works for me on Ubuntu.
Swift 4.2, Ubuntu 16.04
let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = letters.count
var randomString:String = ""
for _ in 0 ..< length {
let rand = Int.random(in: 0..<len)
randomString += letters.map { String($0) }[rand]
}

How should I fix my infinite while loop that takes in 3 conditions? Also stylistic questions for novice

writing code to test the Hailstone Sequence, also called Collatz conjecture. Code will print out the number of iterations of the Hailstone sequence.
def main():
start_num = eval (input ("Enter starting number of the range: "))
end_num = eval (input ("Enter ending number of the range: "))
The main problem is that my code returns an infinite loop. I want to check all of these conditions in one statement
while (start_num > 0 and end_num > 0 and end_num > start_num):
cycle_length = 0
max_length = 0
max_number = 0
my code seems inefficient, there is probably a better way to approach the problem
for i in range(start_num, (end_num + 1)):
cycle_length = 0
while (i != 1):
if (i % 2 == 0):
i = i // 2
cycle_length += 1
if (i % 2 == 1):
i = ((3 * i) + 1)
cycle_length += 1
print (cycle_length)
I just started coding, and I always know that there is a more efficient way to approach these problems. Any suggestions on methodology, problem solving, or stylistic advice would be greatly appreciated.
Here is an answer in java. I assume that we will not start with 1.
public static void main(String[] args) {
int counter =0;
Scanner sc = new Scanner(System.in);
System.out.println("Give us a number to start with:");
int start = sc.nextInt();
System.out.println("Give us a number to end with:");
int end = sc.nextInt();
if (end > start) {
for (int i = 0; i <= end - start; i++) {
counter = 0;
int num = start + i;
int temp = num;
while(temp != 1) {
if ( temp % 2 == 0 ) {
temp = temp / 2;
} else {
temp = 3* temp +1;
}
counter++;
}
System.out.println(num + " takes " + counter + "iterations.");
}
} else {
System.out.println("Your numbers do not make sense.");
}
}
Here's an answer in python in case you're staying up late trying to solve this problem. :P Have a good night.
start_num = 1
end_num = 10
for i in range(start_num, (end_num + 1)):
cycle_length=0
num = i
while (num != 1):
if (num % 2 == 0):
num = num // 2
cycle_length+=1
else:
num = ((3 * num) + 1)
cycle_length+=1
print(cycle_length)

Groovy: indexes of substrings?

How do I find the indexes of all the occurances of a substring in a large string -
(so basically ,and extension of the "indexOf" function) . Any ideas?
Current situation:
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexOf(sub)
// = 9
I want something like:
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexesOf(sub)
// = [9,15]
Is there such a function? How should I implement it otherwise? (in a non trivial way)
You could write a new addition to the String metaClass like so:
String.metaClass.indexesOf = { match ->
def ret = []
def idx = -1
while( ( idx = delegate.indexOf( match, idx + 1 ) ) > -1 ) {
ret << idx
}
ret
}
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexesOf(sub)
There is nothing I know of that exists in groovy currently that gets you this for free though
This is a relatively easy approach:
String.metaClass.indexesOf = { arg ->
def result = []
int index = delegate.indexOf(arg)
while (index != -1) {
result.add(index);
index = delegate.indexOf(arg, index+1);
}
return result;
}
Note that this will find overlapping instances (i.e. "fooo".indexesOf("oo") will return [1, 2]). If you don't want this, replace index+1 with index+arg.length().

Groovy++ error when adding #Typed

I was interested in testing performance gain for groovy++ over plain groovy.
I found the script to test
class Chain
{
def size
def first
def init(siz)
{
def last
size = siz
for(def i = 0 ; i < siz ; i++)
{
def current = new Person()
current.count = i
if (i == 0) first = current
if (last != null)
{
last.next = current
}
current.prev = last
last = current
}
first.prev = last
last.next = first
}
def kill(nth)
{
def current = first
def shout = 1
while(current.next != current)
{
shout = current.shout(shout,nth)
current = current.next
}
first = current
}
}
class Person
{
def count
def prev
def next
def shout(shout,deadif)
{
if (shout < deadif)
{
return (shout + 1)
}
prev.next = next
next.prev = prev
return 1
}
}
def main(args)
{
println "Starting"
def ITER = 100000
def start = System.nanoTime()
for(def i = 0 ; i < ITER ; i++)
{
def chain = new Chain()
chain.init(40)
chain.kill(3)
}
def end = System.nanoTime()
println "Total time = " + ((end - start)/(ITER * 1000)) + " microseconds"
}
It works. But if I try to add
#Typed
before first class name and run I'm getting error:
#groovy groovy.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/home/melco/test/groovy.groovy: 18: Cannot find property next of class Object
# line 18, column 22.
last.next = current
^
1 error
# groovy -version
Groovy Version: 1.7.5 JVM: 1.6.0_18
Any ideas why?
To enjoy statically typed compilation you need to provide at least some amount of type information.
Normally it is enough to define types of properties (next, prev in your case) and types of method parameters.
All the variables you declare are of type java.lang.Object (Or grovy.lang.Object in this case). So they don't have the methods "next" etc.
Try to use Person current = new Person() and Cain current = first etc.

Resources