I've got this legacy code I'm analyzing:
If (X) then
if Cnt < 4 then Cnt = Cnt + 1 ; 4 samples
Else
if Cnt > 0 then Cnt = Cnt-1 ; keep history
EndIf
Which has Cnt go up and down depending on X
And I'm wondering if that else statement acts like their indention implies they think it does.
The code might be interpreted more like:
If (X) then
if Cnt < 4 then
Cnt = Cnt + 1 ; 4 samples
Else
if Cnt > 0 then
Cnt = Cnt-1 ; keep history
EndIf
In which Cnt get to 4 and then toggles on/off if X is true.
This is basic as compiled using BCI51. That's a basic compiler for an 8051 from back in 1990 by Systronix.
How do nested if-else pairs get resolved in basic?
I remember how QBasic did so, and I'm going to assume that this complier is doing the same. This is really tugging on my memory, so I might be wrong.
If a IF THEN is followed by code on the same line, then it is fully contained. Therefore
if Cnt < 4 then Cnt = Cnt + 1
else
...
would be illegal and you must place the Cnt = Cnt + 1 on it's own line to create a multi-line IF statement. Therefore, the ELSE is paired the topmost IF
Since, in the original code, the Cnt = Cnt + 1 and Cnt = Cnt - 1 are on the same lines as the IF THEN, I would interpret the code as follows:
If (X) then
If Cnt < 4 Then
Cnt = Cnt + 1 ; 4 samples
EndIf
Else
If Cnt > 0 Then
Cnt = Cnt-1 ; keep history
EndIf
EndIf
So, yes, I believe the code operates as the indentation implies.
Are you able to modify the code and test if you see any changes?
Related
I would like to know how to increase variables in Nim by 1, alike the (variablename)++ function in other programming languages.
My Code by far:
int i = 1
int j = 1
for i in countup(1, 10):
j = j + 1
echo "number: "
echo i
I want to change the j = j + 1 with the Nim version of: j++
You can use the += operator:
j += 1
You can increment a variable by one in Nim with the inc() function: inc(var_name)
I have seen different solutions to the same problem, but none of them seem to use the approach I used. So here I'm trying to solve the classical coin-change problem in a bottom up dynamic programming approach using a DP table.
int[][] dp = new int[nums.length+1][target+1];
for (int i = 0; i <= nums.length; ++i)
dp[i][0] = 1;
for (int i = 1; i < dp.length; ++i) {
for (int j = 1; j < dp[i].length; ++j) {
if (nums[i-1] <= j)
dp[i][j] = dp[i-1][j] + dp[i][j-nums[i-1]];
else
dp[i][j] = dp[i-1][j];
}
}
The above code generates the table. For fun if I have: {2,3,5} coins, and want to exchange 8, the table would look like:
1 0 0 0 0 0 0 0 0
1 0 1 0 1 0 1 0 1
1 0 1 1 1 1 2 1 2
1 0 1 1 1 2 2 2 3
For the above the following method seem to be working well:
current <- 4
while (current > 0) do
i <- current
j <- 8
while (j > 0) do
if (dp[i][j] != dp[i-1][j]) then
nums[i-1] is part of the current solution
j <- j-1
else
i <- i-1
end
end
current <- current-1
end
Walking through for the above example, we get the following solutions:
1) [5,3]
2) [3,3,2]
3) [2,2,2,2]
Which is great! At least I thought, until I tried: {1,2} with a T=4. For this the table looks like:
1 0 0 0 0
1 1 1 1 1
1 1 2 2 3
With the above algorithm to print all solutions, I only get:
[2,2]
[1,1,1,1]
Which means I won't recover [2,1,1]. So this question is not about the generic how to print the solutions for different approaches to this problem, but how can I read the above DP table to find all solutions.
Well I have one solution but sure... I can see why the other similar answers are using different approaches for this problem. So the algorithm to generate all the possible answers from the above table looks like:
private List<List<Integer>> readSolutions(int[] nums, int[][] dp, int currentRow, int currentColumn, List<Integer> partialSolution) {
if (currentRow == 0) {
return new ArrayList<>();
}
if (currentColumn == 0) {
return new ArrayList<List<Integer>>(Collections.singleton(partialSolution));
}
List<List<Integer>> solutions = new ArrayList<>();
if (dp[currentRow][currentColumn] != dp[currentRow-1][currentColumn]) {
List<Integer> newSolution = new ArrayList<>(partialSolution);
newSolution.add(nums[currentRow-1]);
solutions.addAll(readSolutions(nums, dp, currentRow, currentColumn-nums[currentRow-1], newSolution));
solutions.addAll(readSolutions(nums, dp, currentRow-1, currentColumn, partialSolution));
return solutions;
}
return readSolutions(nums, dp, currentRow-1, currentColumn, partialSolution);
}
The logic on the other hand is simple. Take the above table for example:
0 1 2 3 4
0 1 0 0 0 0
1 1 1 1 1 1
2 1 1 2 2 3
To get a single solution we start from the bottom right corner. If the value does match with the value directly above us, we move up. If it doesn't we move left by the amount corresponding to the row we're in. To generate all answers on on the other hand from the above table...
we're at some position (i,j)
if the value at (i-1,j) is the same as (i,j) we make a recursive call to (i-1,j)
if the values do not match, we have 2 choices...
we can use the number corresponding to the current row, and recurse into (i,j-n)
we can skip the number and check if we can create (i,j) instead by using a recursive call to (i-1,j) instead.
if we reach the first row, we return an empty list
if we reach the first column, we return what we have already found, which will have the sum of target.
Looks awful right, but works as expected.
I have the following code:
let counter n =
let rec count i =
if i > n
then ()
else
print_int i;
count (i+1)
in count 0
It should simply output all numbers from 0 to n. To clarify, I know there are easier ways to achieve the same result but I want to know why it is not working in this specific case.
When I run this code with some parameter eg. counter 5 it does not terminate.
Instead when I change the last line of my code in count 0 to in Thread.create count 0 it outputs 012345
Can someone explain this behaviour?
EDIT
Also found that if you modify the code to this:
let counter n =
let rec count i =
if i > n
then ()
else
let i = i
in print_int i;
count (i+1)
in count 0
it works fine. Why is this?
Your indentation is misleading; your code does
if i > n then () else print_int i;
first and then
count (i+1)
Of course it doesn't terminate! What you want is
else begin
print_int i;
count (i+1)
end
(or else ( ... )). See e.g. "Using begin ... end" in https://ocaml.org/learn/tutorials/if_statements_loops_and_recursion.html.
I need to remove the numeric part at the end of a string. Here are some examples:
"abcd1234" -> "abcd"
"a3bc45" -> "a3bc"
"kj3ih5" -> "kj3ih"
You get the idea.
I implemented a function which works well for this purpose.
Function VarStamm(name As String) As String
Dim i, a As Integer
a = 0
For i = Len(name) To 1 Step -1
If IsNumeric(Mid(name, i, 1)) = False Then
i = i + 1
Exit For
End If
Next i
If i <= Len(name) Then
VarStamm = name.Substring(0, i - 1)
Else
VarStamm = name
End If
End Function
The question is: is there any faster (more efficient in speed) way to do this? The problem is, I call this function within a loop with 3 million iterations and it would be nice to have it be more efficient.
I know about the String.LastIndexOf method, but I don't know how to use it when I need the index of the last connected number within a string.
You can use Array.FindLastIndex and then Substring:
Dim lastNonDigitIndex = Array.FindLastIndex(text.ToCharArray(), Function(c) Not char.IsDigit(c))
If lastNonDigitIndex >= 0
lastNonDigitIndex += 1
Dim part1 = text.Substring(0, lastNonDigitIndex)
Dim part2 = text.Substring(lastNonDigitIndex)
End If
I was skeptical that the Array.FindLastIndex method was actually faster, so I tested it myself. I borrowed the testing code posted by Amessihel, but added a third method:
Function VarStamm3(name As String) As String
Dim i As Integer
For i = name.Length - 1 To 0 Step -1
If Not Char.IsDigit(name(i)) Then
Exit For
End If
Next i
Return name.Substring(0, i + 1)
End Function
It uses your original algorithm, but just swaps out the old VB6-style string methods for newer .NET equivalent ones. Here's the results on my machine:
RunTime :
- VarStamm : 00:00:07.92
- VarStamm2 : 00:00:00.60
- VarStamm3 : 00:00:00.23
As you can see, your original algorithm was already quite well tuned. The problem wasn't the loop. The problem was Mid, IsNumeric, and Len. Since Tim's method didn't use those, it was much faster. But, if you stick with a manual for loop, it's twice as fast as using Array.FindLastIndex, all things being equal
Given your function VarStamm and Tim Schmelter's one named VarStamm2, here is a small test performance I wrote. I typed an arbitrary long String with a huge right part, and ran the functions one million times.
Module StackOverlow
Sub Main()
Dim testStr = "azekzoerjezoriezltjreoitueriou7657678678797897898997897978897898797989797"
Console.WriteLine("RunTime :" + vbNewLine +
" - VarStamm : " + getTimeSpent(AddressOf VarStamm, testStr) + vbNewLine +
" - VarStamm2 : " + getTimeSpent(AddressOf VarStamm2, testStr))
End Sub
Function getTimeSpent(f As Action(Of String), str As String) As String
Dim sw As Stopwatch = New Stopwatch()
Dim ts As TimeSpan
sw.Start()
For i = 1 To 1000000
f(str)
Next
sw.Stop()
ts = sw.Elapsed
Return String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10)
End Function
Function VarStamm(name As String) As String
Dim i, a As Integer
a = 0
For i = Len(name) To 1 Step -1
If IsNumeric(Mid(name, i, 1)) = False Then
i = i + 1
Exit For
End If
Next i
If i <= Len(name) Then
VarStamm = name.Substring(0, i - 1)
Else
VarStamm = name
End If
End Function
Function VarStamm2(name As String) As String
Dim lastNonDigitIndex = Array.FindLastIndex(name.ToCharArray(), Function(c) Not Char.IsDigit(c))
If lastNonDigitIndex >= 0 Then
lastNonDigitIndex += 1
Return name.Substring(0, lastNonDigitIndex)
End If
Return name
End Function
End Module
Here is the output I got:
RunTime :
- VarStamm : 00:00:38.33
- VarStamm2 : 00:00:02.72
So yes, you should choose his answer, his code is both pretty and efficient.
Have a loop in QB64 concerning loop optimization:
DIM N AS DOUBLE, X(100000000) AS DOUBLE
T! = TIMER
FOR N = 1 to 100000000
IF X(N) THEN
PRINT X(N)
EXIT FOR
END IF
NEXT
PRINT TIMER - T!
is it any faster than:
DIM N AS DOUBLE, X(100000000) AS DOUBLE
T! = TIMER
FOR N = 1 to 100000000
IF X(N) <> 0 THEN
PRINT X(N)
EXIT FOR
END IF
NEXT
PRINT TIMER - T!
EDITED: 09-18-2018 to include variable types
I written this code to evaluate your test:
REM Delete REM to enable console runs
REM $CONSOLE:ONLY
REM _DEST _CONSOLE
DIM SHARED N AS DOUBLE, X(100000000) AS DOUBLE
S# = 0: ZC% = 0
T% = 10
IF COMMAND$ <> "" THEN
T% = VAL(COMMAND$)
END IF
IF T% > 999 THEN T% = 999
FOR I% = 1 TO T%
A# = TRYA
B# = TRYB
D# = A# - B#
PRINT USING "Case A ... : #.########"; A#
PRINT USING "Case B ... : #.########"; B#
PRINT USING "Diff ..... : #.########"; D#;
A$ = ""
IF ABS(D#) < 0.00000001 THEN
ZC% = ZC% + 1
A$ = "*"
END IF
S# = S# + A# - B#
PRINT A$
PRINT
REM INKEY$ doesn't work in console mode!
A$ = INKEY$
IF A$ = CHR$(27) THEN
I% = I% + 1: EXIT FOR
END IF
NEXT
PRINT USING "Avrg A - B : #.########"; S# / (I% - 1)
PRINT USING "0 diff:### on ### tryes"; ZC%, (I% - 1)
PRINT
PRINT "Hit a key to exit!"
REM INPUT$ doesn't work in console mode!
A$ = INPUT$(1)
SYSTEM
FUNCTION TRYA#
T# = TIMER
FOR N = 1 TO 100000000
IF X(N) THEN
PRINT X(N)
EXIT FOR
END IF
NEXT
A# = TIMER - T#
TRYA = A#
END FUNCTION
FUNCTION TRYB#
T# = TIMER
FOR N = 1 TO 100000000
IF X(N) <> 0 THEN
PRINT X(N)
EXIT FOR
END IF
NEXT
A# = TIMER - T#
TRYB = A#
END FUNCTION
The two different routines are inserted into two functions: TRYA and TRYB.
I launched this SW with a loop that runs 999 times the functions and the result is:
Avrg. A - B: 0.00204501
0 diff:359 on 999 tryes
then I launched with a 10 times loop and the result is:
Avrg. A - B: -.01640625
0 diff: 1 on 10 tryes
then I launched with a 15 times loop and the result is:
Avrg. A - B: 0.00026042
0 diff: 5 on 15 tryes
Cause we launch the SW in a multi-thread ambient I don't believe this is a very good test, but there's some results:
In two cases the results of no difference (0 diff) is a third of all loops.
In two cases it seems the function TRYA is slower.
In one case it seems the function TRYB is slower.
Looking at these results, I think, we may consider the two functions equivalent!
You obtain more than 10 loops running the code from command line (or modifying the command$ parameter into the QB64 menu) as:
# ./test n
Where n is the number of loops you desire.
The SW was compiled using gcc with -O3 optimizations option. (To do this you have to modify the file [/opt/]qb64/internal/c/makeline_lnx.txt)