Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Wouldn't it be nice... i++ in VFP 1

Status
Not open for further replies.

GriffMG

Programmer
Mar 4, 2002
6,288
1
38
FR
I think Alaska xBase++ supports the C style increment/decrement...

It would have been nice to have that... I've just been inserting columns into a wide excel spreadsheet and having to
manually increment the indexes to the column number (because this was not anticipated in my original design - I thought new columns would just go on the end)
total PITB (especially as I don't have a column editor).

So, as the client is now going to do this daily (most likely) I've done this:

Code:
FUNCTION INC(m.index as integer)
	LOCAL M.OLDINDEX
	M.OLDINDEX = M.INDEX
	M.INDEX = M.INDEX+1
	RETURN(M.OLDINDEX)

Code:
m.COLNO = 1
m.OBJEXCEL.CELLS(m.ROWNO,INC(@M.COLNO)).VALUE = myTable.Myfieldx
m.OBJEXCEL.CELLS(m.ROWNO,INC(@M.COLNO)).VALUE = myTable.Myfieldy
m.OBJEXCEL.CELLS(m.ROWNO,INC(@M.COLNO)).VALUE = myTable.Myfieldz

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
VFP has simulated this for many, many years...! In the editor, type Griff++ and press Space...

To make it work, Open Tools->Intellisense Manager->Advanced->Edit Properties and set lExpandCOperators to .T.
 
I knew about that Tore, but I think that is slightly different.

It expands m.ColNo ++ to m.ColNo = m.ColNo + 1

I want to use m.ColNo as it was, then increment it by one.

I don't really want this:

Code:
m.COLNO = 1
m.OBJEXCEL.CELLS(m.ROWNO,M.COLNO).VALUE = myTable.Myfieldx
m.COLNO = m.COLNO + 1
m.OBJEXCEL.CELLS(m.ROWNO,M.COLNO).VALUE = myTable.Myfieldy
m.COLNO = m.COLNO + 1
m.OBJEXCEL.CELLS(m.ROWNO,M.COLNO).VALUE = myTable.Myfieldz 
m.COLNO = m.COLNO + 1

That said, I am just being a pedant, liking the C syntax...



Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Thanks, it's a nice idea.

It could be reduced a bit:
Code:
FUNCTION INC(INDEX as integer)
	M.INDEX = M.INDEX+1
	RETURN M.INDEX-1
That's not saying it's wrong to introduce the OLDINDEX variable, as it spares a calculation. I'm not even sure whether declaring and setting a variable takes longer than the minus operation. The corner case where m.index is the highest integer fails in both ways.

It was not straightforward for me to see why returning the passed-in value is needed. But otherwise, the implicit return value would neither be the old nor the new value, it would be .T. You do a postincrement, so yes, you want to use the current (old) value and have index incremented for the next usage of it.

++i could also be done:
Code:
FUNCTION PREINC(INDEX as integer)
	M.INDEX = M.INDEX+1
	RETURN M.INDEX

And why not use c++ style for naming the functions, too? We can't use + in names, but if you know c++ source files are having the file extension cpp for c plus plus, then we could call these two increment variations ipp() and ppi(), using i to indicate integer, index or the usual name for an integer loop/iteration variable. And then also imm and mmi for the decrements:

Code:
FUNCTION Ipp(INDEX as integer)
	M.INDEX = M.INDEX+1
	RETURN M.INDEX-1

FUNCTION ppI(INDEX as integer)
	M.INDEX = M.INDEX+1
	RETURN M.INDEX

FUNCTION Imm(INDEX as integer)
	M.INDEX = M.INDEX-1
	RETURN M.INDEX+1

FUNCTION mmI(INDEX as integer)
	M.INDEX = M.INDEX-1
	RETURN M.INDEX

Just by the way, you can type c++ in an editor or in the command window and then space, which triggers an intellisense script to make c = c + 1 from it. But you can't use that trick for a paramter. Really nice.

Chriss
 
I didn't think of decrementing after incrementing etc. Thank you Chris

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
I think in either Alaska xBase++ or perhaps in latter versions of Clipper you could actually extend the language to execute
code blocks as a compiler pre instruction thing - defining that i++ uses and increments i

** Update
In clipper 5.0 you had .ch files that had commands like this in them

Code:
***
*  System SETs
*

#command SET EXACT <x:ON,OFF,&>         => Set( _SET_EXACT, <(x)> )
#command SET EXACT (<x>)                => Set( _SET_EXACT, <x> )

#command SET FIXED <x:ON,OFF,&>         => Set( _SET_FIXED, <(x)> )
#command SET FIXED (<x>)                => Set( _SET_FIXED, <x> )

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
In VFP you only have #DEFINE, and that can do a bit more than just #DEFINE CONSTANT value, but not enough to do this form of macro definition. You could override the Intellisense script to do the transformation differently.

This is handled by _Codesense which points to Foxcode.app and the source code for it is in xsource, if you downloaded that.

The foxcode.pjx has a foxcode.prg which besides others has this code for handling the c operators:
Code:
PROCEDURE HandleCOps()
	* Special script handler for C++ type operators (++,--,+=,-=,*=,/=)
	LOCAL lnWordCount,lcNewWord,lclastWord,laOps,i,lnPos
	LOCAL lcVarName,lcPrefix,lcOpWord,lcSuffix
	lnWordCount=GETWORDCOUNT(THIS.cLine)
	IF VARTYPE(THIS.lExpandCOperators)#"L" OR !THIS.lExpandCOperators OR lnWordCount>2
		RETURN .F.
	ENDIF
	lclastWord = GETWORDNUM(THIS.cLine,lnWordCount)
	lcNewWord = ""
	DIMENSION laOps[8]
	laOps[1] = "++"
	laOps[2] = "--"
	laOps[3] = "+="
	laOps[4] = "-="
	laOps[5] = "*="
	laOps[6] = "/="
	laOps[7] = "^="
	laOps[8] = "%="
	FOR i = 1 TO ALEN(laOps)
		lnPos = ATC(laOps[m.i],lclastWord)
		IF lnPos > 0
			lcVarName = LEFT(lclastWord,lnPos-1)
			IF EMPTY(lcVarName) AND lnWordCount>1
				lcPrefix = GETWORDNUM(THIS.cLine,lnWordCount-1)
			ELSE
				lcPrefix = lcVarName
			ENDIF
			lcOpWord = SUBSTR(lclastWord,lnPos,2)
			lcSuffix = SUBSTR(lclastWord,lnPos+2)
			EXIT
		ENDIF
	ENDFOR
	DO CASE
	CASE lnPos=0 OR (!EMPTY(lcVarName) AND lnWordCount=2)		&& nothing to do		
	CASE (EMPTY(lcVarName) AND lnWordCount=1) OR (!ISALPHA(lcVarName) AND !EMPTY(lcVarName))	&& bad entry, skip it
	CASE INLIST(lcOpWord,"++","--") AND !EMPTY(lcSuffix)			&& skip if anything after ++, --
	CASE INLIST(RIGHT(lcVarName,1),"'","(","[")		&& nothing	
	OTHERWISE
		DO CASE
		CASE INLIST(lcOpWord,"++","--")
			lcSuffix = " " + LEFT(lcOpWord,1) + " 1"
		OTHERWISE
			lcSuffix = " " + LEFT(lcOpWord,1) + IIF(EMPTY(lcSuffix),""," ") + lcSuffix
		ENDCASE
		lcPrefix = CHRTRAN(lcPrefix,"?","")
		lcVarName = lcVarName + IIF(EMPTY(lcVarName),""," ")
		lcNewWord = lcVarName + "= " + lcPrefix + lcSuffix
		THIS.ReplaceWord(lcNewWord)
		RETURN
	ENDCASE
	RETURN .F.
ENDPROC

There you can see it also expands usage of +=, for example, and many more. You could change that so that typing i++ and SPACE will not be changed to i=i+1 but to INC(@i) or Ipp(@i), especially if it's not standalone but there is more code before it. That's possible, though not very simple. It's also possible to act on prefix notation, as you can see from the first few lines of that, THIS.cLine gives you the current line of the code editor (or command window) to react to it.

I'm not sure VFP by default installs xsource.zip, but you can find the latest version of that in
Chriss
 
I've never looked in the intellisense code Chris, might be a good way to do it

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
You could even correct some crap it does. For example, if you write something like
Code:
Str(i++
and then a space, it expands that to
Code:
Str(i = Str(i + 1
Well, it never was intended to do a replacement at the end of a line with any prefix code. But it will detect [tt]Str(i[/tt] as variable name. It also fails, because GETWORDNUM() isn't really a good tool for parsing "words" of source code. There is an intent to detect non-variable names by checking [tt]INLIST(RIGHT(lcVarName,1),"'","(","[")[/tt], but that's not working out for such cases. You'd also need to program this case for allowing prefix operators: [tt]CASE INLIST(lcOpWord,"++","--") AND !EMPTY(lcSuffix)[/tt] [highlight #FCE94F]&& skip if anything after ++, --[/highlight]. But that's the case with prefix operators, of course.

And, by the way, I wouldn't even rant about MS or the VFP team doing this. It's a good example of the "good enough" development pattern.

Chriss
 
Griff,

I recommend you get to know the Intellisense editor. It can open the door to a lot of useful time-savers - not just the i++ stuff we are talking about in this thread. I'll give you a couple of links to learn more:



By the way, you're right about code blocks in Clipper (from ver. 5.0 if remember right). In fact, I think they were one of the best of the innovations in that version.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Thanks Mike, just speed read Dougs paper, I can see there is scope there to do loads.

** update

I have done a tiny, tiny, bit in intellisense, I do a lot of vbscript in the vfp IDE
so I added END<space> and made it expand to END IF


Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
You can do a lot with little scripts, just within the foxcode.dbf, but for this, you need to dig into the foxcode.app, because this is all goverened by one Foxcode record that determines the reaction to typing a space:

intellisense_space_grgfqz.png


There's no point in changing the code here within foxcode.dbf, as space triggers a lot of different intellisense behavior and you don't want to mess with that. But this is the origin of getting to the code in the foxcode.app I pasted above. As said _CODESENSE ( you see it used in this script) is a system variable that's set to foxcode.app and that's what is finally triggered from entering a space.

It's the natural thing to change the already established code within the foxcode.pjx which is in the Tools/XSource directory, as that already is there to handle the C operator syntax expansion.

So changing that is a little more challenging than just adding a new intellisense script record, but the good thing is this is still all VFP code, you can hook into what's already programmed and change it.





Chriss
 
The other thing you can do is use an access method of a property, like this:

Code:
m.i = CreateObject("counter")
? m.i.Value
? m.i.Value
? m.i.Value

Define Class counter as Custom 
   Value = 1
   Procedure Value_access
      This.Value = This.Value + 1
      Return This.Value -1   
   EndProc 
EndDefine
It's shorter in usage, but you need to know that successive access to the value increments it, i.e. ulike a function name INC or Ipp acting on @variable there's no indication in the usage code that .i.Value changes, at first glimpse you'd say this prints the same number 3 times.


Chriss
 
Nifty

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Nifty, but it turns out to be the slowest of all.

Code:
#Define ITERATIONS 2000000
Clear
Local lnI, lnIndex, loCounter

s = Seconds()
For lnI=1 To ITERATIONS
Endfor lnI
e1 = Seconds()

lnIndex = 1
For lnI=1 To ITERATIONS
   =Inc(@m.lnIndex)
Endfor lnI
e2 = Seconds()

lnIndex = 1
For lnI=1 To ITERATIONS
   =Ipp(@m.lnIndex)
Endfor lnI
e3 = Seconds()

loCounter = CreateObject("counter")
For lnI=1 To ITERATIONS
   dummy=loCounter.iValue
Endfor lnI
e4 = Seconds()

? e1-s, 'empty loop'
? e2-e1-(e1-s), 'Inc'
? e3-e2-(e1-s), 'Ipp'
? e4-e3-(e1-s), 'Counter Class'

Function Inc(m.index As Integer)
   Local M.OldIndex
   m.OldIndex = M.index
   m.index = M.index+1
   Return M.OldIndex

Function Ipp(Index As Integer)
   m.index = M.index+1
   Return M.index-1

Define Class Counter as Custom 
   iValue = 1
   Procedure iValue_access
      This.iValue = This.iValue + 1
      Return This.iValue -1   
   EndProc 
EndDefine

So properties are slower to access than variables, and creating a variable to remember the oldindex value is slower than subtracting 1 after adding 1.

But Inc and Ipp are very similar, only 1-2% difference, but the property with access method takes about twice the time.

Chriss
 
For this exercise Ipp is good enough

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top