Welcome, Guest
Username: Password: Remember me
This public forum is meant for questions and discussions about Visual FoxPro
  • Page:
  • 1
  • 2

TOPIC:

Let's write the EVL() function for X#... 08 May 2020 01:46 #14443

  • FoxProMatt
  • FoxProMatt's Avatar
  • Topic Author



Hey all you VFP devs - Let's help the X# Dev Team spend their time working on really important stuff, while we write a few missing Runtime functions. I'm not expert at this X# stuff yet, but it doesn't have our beloved EVL() function, so I started poking away at one that seems to work well so far. If we get something that works in our testing, maybe X# Dev Team will include it in one of the upcoming releases.

I've two things so far: an actual EVL() function, and another TextEVL() function which makes multiple calls to EVL() with various data types. I've only hit the Int, String, and Logical data types so far, but maybe someone else can join me in getting this completes. Let's see what we can do!
// Early development of FoxPro EVL() Function by Matt Slay.
// Dear community - please help my finish this with any missing data types or
// even determine if there is a better way to implement this Function for X# Runtime.

// Discuss on this X# forum thread: https://www.xsharp.eu/forum/public-vfp/1923-let-s-write-the-evl-function-for-x

Function Evl(uValue, uReturnValue)
	
	Var dataType = ValType(uValue) // Using native X# function call here.
	
	Do Case
	    Case dataType = "C"
		    If Alltrim(uValue) == ""
			    Return uReturnValue
		    Else
			    Return uValue
			Endif
		Case dataType = "N"
			If uValue = 0
				Return uReturnValue
			Else
				Return uValue
			Endif
		Case dataType = "L"
			If uValue
				Return .t.
			Else
				Return uReturnValue
			EndIf
	End Case
	
End Function


Test function:
*-- This function tests the  EVL() function
Function TestEvl() As Logic
	
	Local varIntZero, varIntNonZero
	Local varDecimalZero, varDecimalNonZero
	Local varEmptyString, VarNotEmptyString
	Local varDate
	Local varDateTime
	Local varObject
	Local varNull
	Local lPassed
	
	lPassed = .t.
	
	varIntZero = 0
	VarIntNonZero = 1

	// Integer 0
	If Evl(varIntZero, 1) = 1
		? "Evl() on integer Zero Passed."
	Else
		? "Evl() on a integer 0."
		lPassed = .f.
	Endif
	
	// Non-zer Integer
	If Evl(varIntNonZero, 0) = 1 
		? "Evl() on a Non-zero Integer Passed."
	Else
		? "Evl() on a Non-zero Integer FAILED!!!"
		lPassed = .f.
	Endif
	
	// Empty String
	If Evl("", 1) = 1
		? "Evl() on empty String Passed."
	Else
		? "Evl() on empty String FAILED !!!"
		lPassed = .f.
	Endif
	
	// Non-Empty String
	If Evl("Not_Empty_String", 1) = "Not_Empty_String"
		? "Evl() on Non-Empty String Passed."
	Else
		? "Evl() on empty String FAILED !!!"
		lPassed = .f.
	Endif

	// Logical False
	If Evl(.f., 1) = 1
		? "Evl() on logical .F. Passed."
	Else
		? "Evl() on logical .F. FAILED !!!"
		lPassed = .f.
	Endif
		
	// Logical True
	If Evl(.t., 1) = .t.
		? "Evl() on logical .T. Passed."
	Else
		? "Evl() on logical .T. FAILED !!!"
		lPassed = .f.
	Endif
	
	Var oObject = Empty{}
	AddProperty(oObject, "TestProperty", 1)
	// Test by passing a dynamically added property on Empty object
	If Evl(oObject.TestProperty, 2) = 1
		? "Evl() on Dynamically added property Passed."
	Else
		? "Evl() on Dynamically added property Failed !!!"
		lPassed = .f.
	Endif
	

	// ToDo: Test Date & Empty Date
	// Todo: Test Null
	// ToDo: Test DateTime & Empty DateTime 
	// ToDo: Figure out all the other data types that need to be tested.
	
	Wait
	
	Return lPassed
	
End Function


Please Log in or Create an account to join the conversation.

Last edit: by FoxProMatt.

Let's write the EVL() function for X#... 08 May 2020 08:29 #14444

  • mainhatten
  • mainhatten's Avatar


  • Posts: 199
  • Hi Matt,
    not trying to be philosophical, but I am uncertain if your approach is best for xSharp.

    If one aims for code reading a lot like normal xBase for all supported data types, I'd implement Evl() for each datatype combination given as parameter in a separate 1-liner function, resulting in a plethora of simple checks and returns. Both empty and evl() were THAT helpful in vfp because we did not have static (known) types to check directly against, for perf reason sidestepping the function call. For usual datatype alone you probably could shorten to iif(empty(uValue), uReturmValue, uValue).

    IAC when comparing against same data type, switch is better in xSharp, as it does not compile into if-elsif structure needing check at every branch but can directly jump to correct branch like switch in C.

    my 0.02€
    thomas

    Please Log in or Create an account to join the conversation.

    Last edit: by mainhatten.

    Let's write the EVL() function for X#... 08 May 2020 10:13 #14447

    • robert
    • robert's Avatar


  • Posts: 3600
  • Thomas,
    In principle you are right. However I suspect 99% of the code that calls EVL() will have an expression with the first type USUAL. So the only logical thing would be to overload on the 2nd parameter (the default value):
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS LONG) AS USUAL
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS STRING) AS USUAL
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS DATE) AS USUAL
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS FLOAT) AS USUAL
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS CURRENCY) AS USUAL
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS OBJECT) AS USUAL
    etc

    And then each function would look like
    IF IsEmpty(eExpression1 )
       RETURN eExpression2 
    ENDIF
    RETURN eExpression1 
    Is this what you meant ?
    I don't see an advantage over this
    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS USUAL) AS USUAL
    IF IsEmpty(eExpression1 )
       RETURN eExpression2 
    ENDIF
    RETURN eExpression1 

    Btw I noticed that VFP allows this:
    ? EVL("abcde", 1234)
    and returns the "abcde". So the type of the 2 expressions does not have to be the same
    This returns 1234
    ? EVL("        ", 1234)

    But this
    EVL(.NULL., 1234)
    returns .NULL. ???

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Last edit: by robert.

    Let's write the EVL() function for X#... 08 May 2020 11:27 #14448

    • mainhatten
    • mainhatten's Avatar


  • Posts: 199
  • robert wrote:

    EVL(.NULL., 1234)
    returns .NULL. ???

    Hi Robert,
    that one is ok, as .null. is not considered empty and you have the explicit isnull() checking function plus parallel nvl() to evl()
    ? EMPTY(.null.)
    on the rest more later - I don't have firm conviction yet, just some hunches that this will be an area where xSharp and vfp best practice code will be different. For instance for string var/parameter checking with empty function IMO makes sense, not so on boolean or numeric variables.

    Similar the need for different overloaded functions in xSharp for "optional ref parameter" eliminates some of the evl/nvl need necessary in vfp code.

    OTOH just recompiling vfp code and running should be possible, with the option to enhance xSharp later to use benefits of static typing.

    regards
    thomas

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 11:51 #14450

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Robert asked

    But this

    EVL(.NULL., 1234)

    returns .NULL. ???


    In VFP, in general, any function that accepts a .NULL. for one of its arguments returns .NULL. as a result.

    For instance
    MAX(1, .NULL.)
    ABS(.NULL.)
    LEN(.NULL.)
    FV(.NULL., 1, 2)
    FV(100, .NULL., 2)
    FV(100, 1, .NULL.)

    and so on.

    Obvious exceptions are NVL(), ISNULL(), and VARTYPE().

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 11:56 #14451

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Matt

    The ALLTRIM() to test for the emptiness of character expressions must comprehend CHR(9), CHR(10), and CHR(13) also.

    That is
    IF ALLTRIM(uValue, 0, " ", CHR(9), CHR(13), CHR(10)) == ""

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 12:04 #14452

    • robert
    • robert's Avatar


  • Posts: 3600
  • Antonio,

    A minor thing maybe, but I don't think an empty check should call AllTrim() (since that creates a new string). It should be sufficient to walk the list of characters and check for empty / not empty characters.

    Imagine running this check on a string of 64K characters, If you walk the list of chars you can most likely abort the loop because you find a non empty char in the first few characters.

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 12:19 #14453

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Robert

    I don't know how ALLTRIM() is implemented but you're most probably right. ALLTRIM() could be inefficient for large character expressions, and even for smaller ones. But walking through the 1 to LEN(string) positions looking for something outside CHR(32) + CHR(9) + CHR(10) + CHR(13) wouldn't also require constant substring slicing?

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 12:23 #14454

    • robert
    • robert's Avatar


  • Posts: 3600
  • Antonio,
    The string type in .Net has a Chars property which is an indexable collection of characters in the string. No allocation is needed as far as I know to address individual characters .
    The string class is written in CPP:
    You can see here that it simply indexes the character in the buffer
    github.com/dotnet/coreclr/blob/master/sr...stringnative.cpp#L52

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 13:19 #14455

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Robert,

    That seems much more memory efficient. Something for Matt to consider, I'm sure.

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 13:21 #14456

    • FoxProMatt
    • FoxProMatt's Avatar
    • Topic Author



    This discussion is good, letting me see all the things I was NOT thinking about.

    I'm anxious to get the basic VFP functions working soon so that when an old VFP dog learns about X# and they do give it a try but the basic functions are not yet implemented, if will be a big let down for them, and they will there badly of VFP.

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 13:21 #14457

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Matt,

    If X# supports empty dates and datetimes (I'm not working with the machine where I have X# installed right now, so I can't verify this), then YEAR(someDate) or YEAR(someDatetime) should return 0 in case of emptiness.

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 13:25 #14458

    • Zdeněk Krejčí
    • Zdeněk Krejčí's Avatar


  • Posts: 19
  • Is it possible to use generics like c#?
    Zdeněk

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 13:39 #14459

    • lumberjack
    • lumberjack's Avatar


  • Posts: 721
  • Zdeněk Krejčí wrote: Is it possible to use generics like c#?
    Zdeněk

    Yes!
    LOCAL x AS List<String>
    x := List<String>{}
    x:Add("abc")
    x:Add(123) // Error at compile time
    You can also define your own generic classes.
    ______________________
    Johan Nel
    Boshof, South Africa

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 14:07 #14460

    • FoxProMatt
    • FoxProMatt's Avatar
    • Topic Author



    lumberjack - He's talking about using generics to implement EVL().

    Let's not take this thread off on a tangent with code examples of new X# programming examples. X# must implement the EVL() function as-written without users having to change their code, so that's what I want to stay focused on here in this thread.

    Please Log in or Create an account to join the conversation.

    Last edit: by FoxProMatt.

    Let's write the EVL() function for X#... 08 May 2020 15:37 #14461

    • mainhatten
    • mainhatten's Avatar


  • Posts: 199
  • Hi Antonio,

    atlopes wrote:

    In VFP, in general, any function that accepts a .NULL. for one of its arguments returns .NULL. as a result.
    ...
    Obvious exceptions are NVL(), ISNULL(), and VARTYPE().

    "most" would be better. There are string functions not following that rule, but they are few - I had mentioned that missing Nullcheck in xSharp quite a while back. Uncertain if those should be implemented only in vfp dialect calling base implementation, base implementation needs to be enhanced or new implementation (for 1-3 liners, cutting out method call) are appropriate. Will check when new version is available to non-FoXers and if not done perhaps do it in 1 swoop for those I calling my attention early on.

    regards
    thomas

    Please Log in or Create an account to join the conversation.

    Last edit: by mainhatten.

    Let's write the EVL() function for X#... 08 May 2020 15:41 #14462

    • mainhatten
    • mainhatten's Avatar


  • Posts: 199
  • Hi Robert,

    robert wrote: The string type in .Net has a Chars property which is an indexable collection of characters in the string. No allocation is needed as far as I know to address individual characters .
    The string class is written in CPP:
    You can see here that it simply indexes the character in the buffer
    github.com/dotnet/coreclr/blob/master/sr...stringnative.cpp#L52

    interesting - might speed up my GetWord* functions if not already tweaked by Chris or you.

    regards
    thomas

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 16:18 #14466

    • Zdeněk Krejčí
    • Zdeněk Krejčí's Avatar


  • Posts: 19
  • FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS <T>) AS USUAL
    If IsEmpty(eExpression1)
      Return eExpression2
    Endif
    Return eExpression1
    ....

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 16:23 #14467

    • robert
    • robert's Avatar


  • Posts: 3600
  • Zdenek,
    What would the benefit of this be over

    FUNCTION EVL(eExpression1 AS USUAL, eExpression2 AS USUAL) AS USUAL
    If IsEmpty(eExpression1)
    Return eExpression2
    Endif
    Return eExpression1

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    Let's write the EVL() function for X#... 08 May 2020 18:06 #14476

    • atlopes
    • atlopes's Avatar


  • Posts: 84
  • Robert

    The string type in .Net has a Chars property which is an indexable collection of characters in the string. No allocation is needed as far as I know to address individual characters .


    I've been trying this for a bit in the context of Matt EVL() draft. I'm probably missing something, but since Chars is a String collection, this won't work until a) we create a new string from the Usual parameter, and thus facing a string replication problem again; or b) we have a different implementation of the EVL() function for strings.

    Code for option a)
    Case dataType == "C"
    
      LOCAL emptyChars = " " + CHR(9) + CHR(10) + CHR(13) AS String
      LOCAL testString = uValue.ToString() AS String
    
      FOR VAR idx = 0 TO testString.Length - 1
        If (emptyChars.IndexOf(testString.Chars[idx]) == -1)
          Return uValue
        Endif
      NEXT
    
      Return uReturnValue

    Code for option b)
    Function Evl (sValue AS String, uReturnValue)
    
      LOCAL emptyChars = " " + CHR(9) + CHR(10) + CHR(13) AS String
    
      FOR VAR idx = 0 TO sValue.Length - 1
        If (emptyChars.IndexOf(sValue.Chars[idx]) == -1)
          Return sValue
        Endif
      NEXT
    
      Return uReturnValue
    
    End Function

    Handling of .NULL. seems a problem, really, and appearing in various places, as Thomas mentioned. I found out that I can't even issue
    ? .NULL.
    without raising an error.

    Please Log in or Create an account to join the conversation.

    Last edit: by atlopes.
    • Page:
    • 1
    • 2