Page 1 of 3

For Next Step

Posted: Wed Feb 15, 2012 5:15 am
by IdeasVacuum
The Step of a For Next loop is limited to an integer constant. There are occasions where an integer is required instead of a constant.

Edit: Yes, I am using another loop type instead but For Next is more elegant.

Re: For Next Step

Posted: Wed Feb 15, 2012 10:40 pm
by STARGÅTE

Re: For Next Step

Posted: Wed Feb 15, 2012 10:42 pm
by luis
STARGÅTE wrote:SPAM :!:
Already requested ... yes.

But SPAM ? :shock:

Re: For Next Step

Posted: Sat Feb 18, 2012 8:13 am
by charvista
Why not create a ForD, like we have StrD and ValD, for example?
This preserves the original For - Next statement, to maintain high speed.

I second the idea to create a For/Next statement that uses decimals (as well the Step), as it makes programming sometimes much easier.

Re: For Next Step

Posted: Sun Feb 19, 2012 1:29 am
by void
My reading of the original post is not for floating point steps, but variable steps.

for i = x to y step z where z is an actual expression, not a constant.

I can see how this could be useful in some circumstances.

Re: For Next Step

Posted: Sun Feb 19, 2012 4:11 pm
by Tenaja
Yes, very useful in many situations. While I haven't used it yet in PB, I'm surprised to see PB doesn't allow it.

Here are a couple macros that will work with any var type and any Step. They are the same as what For Next does, but without restriction. The only drawback is that since you can't put a block of code in a macro call, you have to reference the Var and Step in the ForEnd, not the ForStart.

Code: Select all

Macro ForStart(Var, starting, ending)
	Var = starting
	While Var <= ending
EndMacro
	
Macro ForEnd(Var, increment)
	Var = Var + increment
	Wend
EndMacro

Macro ForStartDec(Var, starting, ending)
	Var = starting
	While Var >= ending
EndMacro
	
Macro ForEndDec(Var, increment)
	Var = Var - increment
	Wend
EndMacro


; Incrementing sample with Float:
x.f

ForStart(x, 1, 3.5)
	Debug x
ForEnd(x, 0.5)
	
Debug "last x " + Str(x)



; Decrementing sample with variable Step:
i.f
a.i = 2

ForStartDec(i, 12, 2)
	Debug i
ForEndDec(i, a)
	

Debug "last i " + Str(i)

Re: For Next Step

Posted: Tue Feb 21, 2012 12:38 am
by charvista
@Tenaja,
I was very interested in your Macros in attempting to create a For-Next-Step with the same sequence and logic.
I have studied the drawback you mentioned.
Well, I have rewritten the Macros based on your idea.
I have tested it superficially, and it seems to work... Any bug or comment is welcome!

Code: Select all

;==================================================================
Macro Loop(Var,Starting,Ending,Increment=1)
    *Start#Var=AllocateMemory(9)
    PokeD(*Start#Var,Starting)
    *End#Var=AllocateMemory(9)
    PokeD(*End#Var,Ending)
    *Incr#Var=AllocateMemory(9)
    PokeD(*Incr#Var,Increment)
    Var=Starting
    Repeat
EndMacro
;==================================================================
Macro EndLoop(Var)
    Starting#Var.d=PeekD(*Start#Var)
    Ending#Var.d=PeekD(*End#Var)
    Increment#Var.d=PeekD(*Incr#Var)
    Var+Increment#Var
    Qend.d=Starting#Var+((Ending#Var-Starting#Var)/Increment#Var)*Increment#Var; calculate the number of steps to know the real destination value
    a.d=Var : b.d=Qend
    If Increment#Var<0; negative increment
        a.d=Qend : b.d=Var; swap current var & real ending
    EndIf
    Until a>b; if increment was negative, we had to swap the variables, because we cannot swap the direction of Until
    FreeMemory(*Incr#Var)
    FreeMemory(*Start#Var)
    FreeMemory(*End#Var)
EndMacro
;==================================================================
; Example
Loop(i.d,1,5.5,1.5)
    Loop(j.d,16,4,-3.5)
        Debug "("+StrD(i,2)+", "+StrD(j,2)+")"
    EndLoop(j)   
EndLoop(i)

Debug "last i = " + StrD(i)
Debug "last j = " + StrD(j)
;==================================================================

Re: For Next Step

Posted: Tue Feb 21, 2012 5:16 am
by Tenaja
Charvista, in general, I like your use of alloc to make them nestable. On the other hand, have you looked at the asm? It's LOOOOOOONG! So maybe not great for time sensitive code. This is your sample, with no edits:

Code: Select all

; 
; PureBasic 4.60 (Windows - x86) generated code
; 
; (c) 2011 Fantaisie Software
; 
; The header must remain intact for Re-Assembly
; 
; Memory
; :System
; KERNEL32
; :Import
; 
format MS COFF
; 
; 
extrn _PB_AllocateMemory@4
extrn _PB_FreeMemory@4
extrn _PB_FreeMemorys@0
extrn _PB_InitMemory@0
extrn _PB_PeekD@4
extrn _PB_PokeD@12
extrn _ExitProcess@4
extrn _GetModuleHandleA@4
extrn _HeapCreate@12
extrn _HeapDestroy@4
extrn _memset
public _PB_Instance
public _PB_ExecutableType
public _PB_OpenGLSubsystem
public _PB_MemoryBase
public PB_Instance
public PB_MemoryBase
public _PB_EndFunctions

macro pb_public symbol
{
  public  _#symbol
  public symbol
_#symbol:
symbol:
}

macro    pb_align value { rb (value-1) - ($-_PB_DataSection + value-1) mod value }
macro pb_bssalign value { rb (value-1) - ($-_PB_BSSSection  + value-1) mod value }
public PureBasicStart
; 
section '.code' code readable executable align 8
; 
; 
PureBasicStart:
; 
  PUSH   dword I_BSSEnd-I_BSSStart
  PUSH   dword 0
  PUSH   dword I_BSSStart
  CALL  _memset
  ADD    esp,12
  PUSH   dword 0
  CALL  _GetModuleHandleA@4
  MOV    [_PB_Instance],eax
  PUSH   dword 0
  PUSH   dword 4096
  PUSH   dword 0
  CALL  _HeapCreate@12
  MOV    [PB_MemoryBase],eax
  CALL  _PB_InitMemory@0
; 
;==================================================================
; Macro Loop(Var,Starting,Ending,Increment=1)
;==================================================================
; Macro EndLoop(Var)
;==================================================================
; Example
; Loop(i.d,1,5.5,1.5)
  PUSH   dword 9
  CALL  _PB_AllocateMemory@4
  MOV    dword [p_Starti],eax
  FLD    qword [D1]
  SUB    esp,8
  FSTP   qword [esp]
  PUSH   dword [p_Starti]
  CALL  _PB_PokeD@12
  PUSH   dword 9
  CALL  _PB_AllocateMemory@4
  MOV    dword [p_Endi],eax
  FLD    qword [D2]
  SUB    esp,8
  FSTP   qword [esp]
  PUSH   dword [p_Endi]
  CALL  _PB_PokeD@12
  PUSH   dword 9
  CALL  _PB_AllocateMemory@4
  MOV    dword [p_Incri],eax
  FLD    qword [D3]
  SUB    esp,8
  FSTP   qword [esp]
  PUSH   dword [p_Incri]
  CALL  _PB_PokeD@12
  FLD    qword [D1]
  FSTP   qword [v_i]
_Repeat1:
; Loop(j.d,16,4,-3.5)
  PUSH   dword 9
  CALL  _PB_AllocateMemory@4
  MOV    dword [p_Startj],eax
  FLD    qword [D4]
  SUB    esp,8
  FSTP   qword [esp]
  PUSH   dword [p_Startj]
  CALL  _PB_PokeD@12
  PUSH   dword 9
  CALL  _PB_AllocateMemory@4
  MOV    dword [p_Endj],eax
  FLD    qword [D5]
  SUB    esp,8
  FSTP   qword [esp]
  PUSH   dword [p_Endj]
  CALL  _PB_PokeD@12
  PUSH   dword 9
  CALL  _PB_AllocateMemory@4
  MOV    dword [p_Incrj],eax
  FLD    qword [D6]
  SUB    esp,8
  FSTP   qword [esp]
  PUSH   dword [p_Incrj]
  CALL  _PB_PokeD@12
  FLD    qword [D4]
  FSTP   qword [v_j]
_Repeat2:
; Debug "("+StrD(i,2)+", "+StrD(j,2)+")"
; EndLoop(j)   
  PUSH   dword [p_Startj]
  CALL  _PB_PeekD@4
  FSTP   qword [v_Startingj]
  PUSH   dword [p_Endj]
  CALL  _PB_PeekD@4
  FSTP   qword [v_Endingj]
  PUSH   dword [p_Incrj]
  CALL  _PB_PeekD@4
  FSTP   qword [v_Incrementj]
  FLD    qword [v_j]
  FADD   qword [v_Incrementj]
  FADD   qword [D7]
  FSTP   qword [v_j]
  FLD    qword [v_Startingj]
  FLD    qword [v_Endingj]
  FSUB   qword [v_Startingj]
  FADD   qword [D7]
  FDIV   qword [v_Incrementj]
  FMUL   qword [v_Incrementj]
  FADDP  st1,st0
  FADD   qword [D7]
  FSTP   qword [v_Qend]
  FLD    qword [v_j]
  FSTP   qword [v_a]
  FLD    qword [v_Qend]
  FSTP   qword [v_b]
  FLD    qword [v_Incrementj]
  FCOMP  qword [D7]
  FNSTSW ax
  TEST   ah,1h
  JE    _EndIf4
  FLD    qword [v_Qend]
  FSTP   qword [v_a]
  FLD    qword [v_j]
  FSTP   qword [v_b]
_EndIf4:
  FLD    qword [v_a]
  FLD    qword [v_b]
  FCOMPP
  FNSTSW ax
  TEST   ah,1h
  JE    _Repeat2
_Until2:
  PUSH   dword [p_Incrj]
  CALL  _PB_FreeMemory@4
  PUSH   dword [p_Startj]
  CALL  _PB_FreeMemory@4
  PUSH   dword [p_Endj]
  CALL  _PB_FreeMemory@4
; EndLoop(i)
  PUSH   dword [p_Starti]
  CALL  _PB_PeekD@4
  FSTP   qword [v_Startingi]
  PUSH   dword [p_Endi]
  CALL  _PB_PeekD@4
  FSTP   qword [v_Endingi]
  PUSH   dword [p_Incri]
  CALL  _PB_PeekD@4
  FSTP   qword [v_Incrementi]
  FLD    qword [v_i]
  FADD   qword [v_Incrementi]
  FADD   qword [D7]
  FSTP   qword [v_i]
  FLD    qword [v_Startingi]
  FLD    qword [v_Endingi]
  FSUB   qword [v_Startingi]
  FADD   qword [D7]
  FDIV   qword [v_Incrementi]
  FMUL   qword [v_Incrementi]
  FADDP  st1,st0
  FADD   qword [D7]
  FSTP   qword [v_Qend]
  FLD    qword [v_i]
  FSTP   qword [v_a]
  FLD    qword [v_Qend]
  FSTP   qword [v_b]
  FLD    qword [v_Incrementi]
  FCOMP  qword [D7]
  FNSTSW ax
  TEST   ah,1h
  JE    _EndIf6
  FLD    qword [v_Qend]
  FSTP   qword [v_a]
  FLD    qword [v_i]
  FSTP   qword [v_b]
_EndIf6:
  FLD    qword [v_a]
  FLD    qword [v_b]
  FCOMPP
  FNSTSW ax
  TEST   ah,1h
  JE    _Repeat1
_Until1:
  PUSH   dword [p_Incri]
  CALL  _PB_FreeMemory@4
  PUSH   dword [p_Starti]
  CALL  _PB_FreeMemory@4
  PUSH   dword [p_Endi]
  CALL  _PB_FreeMemory@4
; 
; Debug "last i = " + StrD(i)
; Debug "last j = " + StrD(j)
;==================================================================
_PB_EOP_NoValue:
  PUSH   dword 0
_PB_EOP:
  CALL  _PB_EndFunctions
  PUSH   dword [PB_MemoryBase]
  CALL  _HeapDestroy@4
  CALL  _ExitProcess@4
_PB_EndFunctions:
  CALL  _PB_FreeMemorys@0
  RET
; 
; 
section '.data' data readable writeable
; 
_PB_DataSection:
_PB_OpenGLSubsystem: db 0
pb_public PB_DEBUGGER_LineNumber
  dd     -1
pb_public PB_DEBUGGER_IncludedFiles
  dd     0
pb_public PB_DEBUGGER_FileName
  db     0
_PB_ExecutableType: dd 0
align 4
D1: dd 0,1072693248
D2: dd 0,1075183616
D3: dd 0,1073217536
D4: dd 0,1076887552
D5: dd 0,1074790400
D6: dd 0,-1072955392
D7: dd 0,0
align 4
s_s:
  dd     0
  dd     -1
align 4
; 
section '.bss' readable writeable
_PB_BSSSection:
align 4
; 
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rd 1
_PB_Instance:
PB_Instance: rd 1
; 
align 4
v_i rq 1
v_j rq 1
v_Startingj rq 1
v_Endingj rq 1
v_Incrementj rq 1
v_Qend rq 1
v_a rq 1
v_b rq 1
v_Startingi rq 1
v_Endingi rq 1
v_Incrementi rq 1
PB_DataPointer rd 1
p_Starti rd 1
p_Endi rd 1
p_Incri rd 1
p_Startj rd 1
p_Endj rd 1
p_Incrj rd 1
align 4
align 4
align 4
align 4
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection:
That's 25 instructions to get started, and 49 at the end (minus a few labels). That's a lot of overhead for a loop...mine is 10 instructions total (plus labels):

Code: Select all

; ForStart(x, 1, 3.5)
  MOV    dword [v_x],1065353216
_While1:
  FLD    dword [v_x]
  FCOMP  dword [F1]
  FNSTSW ax
  TEST   ah,41h
  JE    _Wend1
; Debug x
; ForEnd(x, 0.5)
  FLD    dword [v_x]
  FADD   dword [F2]
  FSTP   dword [v_x]
  JMP   _While1
_Wend1:
; 
; Debug "last x " + Str(x)

Re: For Next Step

Posted: Tue Feb 21, 2012 5:15 pm
by Tenaja
I was thinking of your loops...the same thing can be accomplished with Push and Pop. That will save most of the code space.

Re: For Next Step

Posted: Tue Feb 21, 2012 5:34 pm
by djes
Maybe like this...

Code: Select all

Macro ForD(StartD)
 StartD
  !Loop@:
EndMacro

Macro NextD(StartD, StopD, StepD)
  StartD + StepD
  If StartD < StopD
    !jmp Loop@
  EndIf
EndMacro

ForD(i.d = 0)
  Debug i.d
NextD(i, 8, 0.5)

Re: For Next Step

Posted: Tue Feb 21, 2012 6:21 pm
by charvista
@Tenaja & @djes & @All
The problem is that I have never learned Assembler, so I have no idea how to make it shorter. I am thinking purely in PureBasic. I can only understand that the complexity of my code can be time consuming at runtime, but I made some tests and I think that the speed is still high enough, as calculations with doubles are always slower than with integers.
@djes: the macro you made has a problem: in nested loops, it cannot be used again, because the symbol is already defined. (As this is asm, I have no clue what that means).

Code: Select all

;==================================================================
Macro Loop(Var,Starting,Ending,Increment=1)
    *Start#Var=AllocateMemory(9)
    PokeD(*Start#Var,Starting)
    *End#Var=AllocateMemory(9)
    PokeD(*End#Var,Ending)
    *Incr#Var=AllocateMemory(9)
    PokeD(*Incr#Var,Increment)
    Var=Starting
    Repeat
EndMacro
;==================================================================
Macro EndLoop(Var)
    Starting#Var.d=PeekD(*Start#Var)
    Ending#Var.d=PeekD(*End#Var)
    Increment#Var.d=PeekD(*Incr#Var)
    Var+Increment#Var
    Qend.d=Starting#Var+((Ending#Var-Starting#Var)/Increment#Var)*Increment#Var; calculate the number of steps to know the real destination value
    a.d=Var : b.d=Qend
    If Increment#Var<0; negative increment
        a.d=Qend : b.d=Var; swap current var & real ending
    EndIf
    Until a>b; if increment was negative, we had to swap the variables, because we cannot swap the direction of Until
    FreeMemory(*Incr#Var)
    FreeMemory(*Start#Var)
    FreeMemory(*End#Var)
EndMacro
;==================================================================
Macro ForD(StartD)
  !Loop@:
EndMacro
;==================================================================
Macro NextD(StartD, StopD, StepD)
  StartD + StepD
  If StartD < StopD
    !jmp Loop@
  EndIf
EndMacro
;==================================================================
; Example

Debug "Please wait until loops are finished!............."
DisableDebugger

StartTime = ElapsedMilliseconds()  
Loop(i.d,1,100000,0.75)
    Loop(j.d,6000,-4000,-3.5)
        ;Debug "("+StrD(i,2)+", "+StrD(j,2)+")"
    EndLoop(j)   
EndLoop(i)
ElapsedTime1 = ElapsedMilliseconds()-StartTime

; StartTime = ElapsedMilliseconds() 
; ForD(a.d = 1)
;     ForD(b.d = 6000)
;         ;...
;     NextD(b, -4000,-3.5)
; NextD(a, 100000, 0.75)
; ElapsedTime2 = ElapsedMilliseconds()-StartTime

StartTime = ElapsedMilliseconds()  
For k=1 To 100000 Step 1
    For l=6000 To -4000 Step -3
        ;Debug "("+StrD(k,2)+", "+StrD(l,2)+")"
    Next l
Next k
ElapsedTime3 = ElapsedMilliseconds()-StartTime

EnableDebugger

Debug ElapsedTime1
;Debug ElapsedTime2
Debug ElapsedTime3

;==================================================================

Re: For Next Step

Posted: Wed Feb 22, 2012 7:41 pm
by blueznl
I don't see any advantage of a for / next with variable step over a while / wend or repeat / until. In fact, a while / wend leaves IMHO better readable code.

Not being able to use a quad in a for / next is indeed a bit silly, though I've never need it thus far :-)

Re: For Next Step

Posted: Wed Feb 22, 2012 9:55 pm
by charvista
@blueznl
Let's return the problem: What's the advantage of For/Next over While/Wend or Repeat/Until ?
If there is one, even a small one, then the same advantage may apply to the variable step (and doubles) as well. :twisted:

To me, the big advantage is that I tell the program the fixed range and the step to the loop in one single line before it begins.

Re: For Next Step

Posted: Tue Feb 28, 2012 8:36 pm
by blueznl
Yes, I can see that advantage of For / Next, and that does make sense. However, when doing fancy stuff (like changing the step parameter) it actually does make sense to switch to While / Wend, as you're no longer doing something simple and straightforward. In fact, you're changing the step parameter, something not obvious from the first For / Next call.

So, I beg to disagree with you :-)

Re: For Next Step

Posted: Tue Feb 28, 2012 9:31 pm
by Tenaja
charvista wrote:To me, the big advantage is that I tell the program the fixed range and the step to the loop in one single line before it begins.
The way PB handles the For/Next loop, the ONLY advantage is readability. It compiles longer, and executes slower than a repeat/until or while/wend.

The reason is the same as what you like about it. PB parses the loop sequentially, and does NOT reorder anything. Therefore, the assignment is made, the test is performed, the increment is Jumped over (wasted time), the loop is run, then at Next, it Jumps to the increment, after which it Jumps to the test, and then it either jumps to the loop code or out of the loop.

All of those jumps make the For-Next loop the most inefficient loop you can use--both in code space and execution time. But it is easy to code and very readable, so if speed is not an issue, all is good.

Oh, yeah...only if your increment is a constant integer, you are good.