Page 1 of 1

understanding OOP returning more than value from a method

Posted: Tue May 04, 2010 8:16 am
by Ehab Samir Aziz
I have calculate method which calculate the age in years . How can I return months and days from that method as well as I return years . Pointers in C used to return more than one value from a function . What is the equalvent in fivewin.
Thanks

Code: Select all

#include "fivewin.ch"

function main()
local oPerson1,oPerson2

set date format to "DD/MM/YYYY"

oPerson1 :=Tperson():new("Enas Samir",ctod("17/02/1967"))
oPerson2 :=Tperson():new("Ehab Samir",ctod("20/02/1972"))

msginfo (oPerson1:cName)
msginfo (oPerson1:dDob)
msginfo (oPerson1:age())
msginfo (oPerson1:calculate())


msginfo (oPerson2:cName)

msginfo (oPerson2:dDob)

msginfo (oPerson2:age())

msginfo (oPerson2:calculate())

return nil

create class TPerson
        var cName
        var dDob
        var nAge_years
        var nAge_months
        var nAge_days
        method new
        method age
        method calculate

endclass





method age
*---------------
::nAge_days:= (date()-::dDob)
return (::nAge_days)

method new(cName,dDob,nAge_years,nAge_months,nAge_days)
*-----------------------------------------------------
default cName:="",dDob:=ctod(" / /    "),nAge_years:=0,nAge_months:=0,nAge_days:=0
::cName:=cName
::dDob:=dDob
::nAge_years:=nAge_years
::nAge_months:=nAge_months
::nAge_days:=nAge_days


return self

method calculate()
*-----------------------
::nAge_years:=int (::nAge_days/365)

::nAge_months:=int (::nAge_days/12)

::nAge_days:=::nAge_days%12

return (::nAge_years)
 

Re: understanding OOP returning more than value from a method

Posted: Tue May 04, 2010 9:33 am
by gkuhnert
After calling the methods age() and calculate() you simply can access the values oPerson1:nAge_Months and oPerson1:nAge_Days

Re: understanding OOP returning more than value from a method

Posted: Tue May 04, 2010 10:13 am
by Ehab Samir Aziz
If I seprated the 3 functions (calculate_years,calculate_months,calculate_days) as not as calculate()

msginfo (oPerson1:calculate_years+ "year" +str (oPerson1:calculate_months())+" month" + str(oPerson1:calculate_days())+" days" )

is not as the same statement :

msginfo (str(oPerson1:nAge_years)+ "year" +str(oPerson1:nAge_months)+" month" + str(oPerson1:nAge_days)+" days" )

Code: Select all

#include "fivewin.ch"

function main()
local oPerson1,oPerson2

set date format to "DD/MM/YYYY"

oPerson1 :=Tperson():new("Enas Samir",ctod("17/02/1967"))
oPerson2 :=Tperson():new("Ehab Samir",ctod("20/02/1972")):calculate()


msginfo ("Name : " + oPerson1:cName + "    Date of Birth : "+ dtoc(oPerson1:dDob) + "     Age : " + str(oPerson1:age())+ " day  ")
msginfo ("Name : " + oPerson2:cName + "    Date of Birth : "+ dtoc(oPerson2:dDob) + "     Age : " + str(oPerson2:age())+ " day  ")

msginfo (" For " + (oPerson1:cName) + str(oPerson1:calculate_years)+ "year" +str (oPerson1:calculate_months())+" month" + str(oPerson1:calculate_days())+" days" )

msginfo (" For " + (oPerson2:cName) +str(oPerson2:nAge_years)+ "year" +str(oPerson2:nAge_months)+" month" + str(oPerson2:nAge_days)+" days" )


return nil

create class TPerson
        var cName
        var dDob
        var nAge_years
        var nAge_months
        var nAge_days
        method new
        method age
        method calculate
        method calculate_years
        method calculate_months
        method calculate_days

endclass





method age
*---------------
::nAge_days:= (date()-::dDob)
return (::nAge_days)

method new(cName,dDob,nAge_years,nAge_months,nAge_days)
*-----------------------------------------------------
default cName:="",dDob:=ctod(" / /    "),nAge_years:=0,nAge_months:=0,nAge_days:=0
::cName:=cName
::dDob:=dDob
::nAge_years:=nAge_years
::nAge_months:=nAge_months
::nAge_days:=nAge_days


return self

method calculate()
*-----------------------
::nAge_years:=int (::nAge_days/365)
::nAge_months:=::nAge_days-(::nAge_years*365)
//? "reaminader months : " + str(::nAge_months)

::nAge_months:=int(::nAge_months/12)
::nAge_days=::nAge_days-(::nAge_years)*365-(::nAge_months*12)
//? "reaminader days : " + str(::nAge_days)


return self


method calculate_years()
*-----------------------
::nAge_years:=int (::nAge_days/365)
return (::nAge_years)


method calculate_months()
*-----------------------

::nAge_months:=::nAge_days-(::nAge_years*365)
//? "reaminader months : " + str(::nAge_months)

::nAge_months:=int(::nAge_months/12)
::nAge_days=::nAge_days-(::nAge_years)*365-(::nAge_months*12)
//? "reaminader days : " + str(::nAge_days)
//? (::nAge_days)
//? (::nAge_months)
//::nAge_days=::nAge_months-int(::nAge_months/12)



//::nAge_months:=int(::nAge_days/12)

return (::nAge_months)

method calculate_days()
*-----------------------
//? (::nAge_days)
//::nAge_days=::nAge_days-(::nAge_months)*30
//? (::nAge_days)
//? (::nAge_months)

return (::nAge_days)
 

Re: understanding OOP returning more than value from a method

Posted: Tue May 04, 2010 12:37 pm
by Adolfo
Ehab...
My personal opinion...

I don't think a CLASS has to be made to fullfil the requirements you want.
just a Simple function With and Array as a return Value.
Simplier and faster...


#define Years 1
#define Months 2
#define Days 3
//-------------------------------------------
Function Whatever(SentDate)
Local Age:=CalculateAge(SentDate)

? Age[Years]
? Age[Months]
? Age[Days]

Return Nil

//-------------------------------------------
Function CalculateAge(SentDate)
Local nYears:=0
Local nMonths:=0
Local nDays:=0

nYears:= ...formula1
nMonths:= ...formula2
nDays:= ...formula3

Return {nYears,nMonths,nDays}

Re: understanding OOP returning more than value from a method

Posted: Wed May 05, 2010 9:51 am
by xProgrammer
Hi all

I would like to add a few comments to this discussion.

Perhaps most importantly the quoted code

Code: Select all

::nAge_years:=int (::nAge_days/365)
will give incorrect results around a person's birthday due to the effect of leap years and should definitely not be used.. Even the following code

Code: Select all

::nAge_years:=int (::nAge_days/365.25)
will give errors for the same reason, although it will give less errors. I gave the best way to calculate age in years in a recent post on this forum dealing with the calculation of number of days. The basic technique is to calculate a "trial"age in years as follows:

Code: Select all

nTrialAge = Year( date_AgeAsAt ) - Year (date_Birth )
This of course is the person's age if their birthday has already occurred in the year of the date you are calculating their age to. If that date has not passed you need to subtract 1 from nTrialAge. The logic for this is easy enough. Firstly compare months, and if they are equal compare days. Something like this:

Code: Select all

IF Month( date_AgeAt ) > Month( date_Birth )
  RETURN nTrialAge 
ENDIF
IF Month( date_AgeAt ) < Month( date_Birth  )
  RETURN nTrialAge - 1
ENDIF
// if we reach here we are in the month of the persons birthday
IF Day( date_AgeAt ) >= Day( date_Birth )
  RETURN nTrialAge 
 ELSE
  RETURN nTrialAge - 1
ENDIF
Secondly with regards to passing the result back.

If the result is ever only going to be displayed you could pass it back in a single string.

You could use an array, a hash would be neater. A better way would probably be to pass by reference

Code: Select all

nYears := 0
nMonths := 0
nDays := 0
CalculateAge( dBirth, Date(), @nYears, @nMonths, @nDays )
Of course you could just set properties of your class and use them, something like

Code: Select all

oPerson:CalculateAge( Date() )
? oPerson:nYears
? oPerson:nMonths
? oPerson:nDays
Using this approach you have to be careful that you don't access these properties without having done the calculation first.

I hope that this post might be of some interest / help.

Regards
xProgrammer

Return age (year,Mnd,day) from a date

Posted: Mon Oct 16, 2017 9:36 pm
by Marc Venken
I found this tread for calculating the age, but It seems that it give no correct values.


I Found also a Exel formula that works pretty ok : = nice result

=ALS(EN(MAAND(B12)=MAAND(A12);DAG(B12)<DAG(A12));J AAR( B12)-JAAR(A12)-1&" jr ";ALS(MAAND(B12)-MAAND(A12)<0;JAAR(B12)-JAAR(A12)-1;JAAR(B12)-JAAR(A12))&" jr " )&ALS(EN(MAAND(B12)=MAAND(A12);DAG(B12)<DAG(A12)); MAAN D(B12)-1-MAAND(A12)+12&" mnd ";ALS(DAG(B12)-DAG(A12)<0;(ALS(MAAND(B12)-MAAND(A12)<0;(MAAND(B12)-MAAND(A12)+12);MAAND(B12)-MAAND(A12)))-1;(ALS(MAAND(B12)-MAAND(A12)<0;(MAAND(B12)-MAAND(A12)+12);MAAND(B12)-MAAND(A12))))&" mnd ")&ALS(DAG(B12)<DAG(A12);((DATUMWAARDE(1&"/"&ALS(MAAND(A12)+1=13;1;MAAND(A12)+1)&"/"&ALS(MAAND(A12)+1=13;JAAR(A12)+1;JAAR(A12)))-(A12+1-DAG(A12)))+DAG(B12)-DAG(A12));DAG(B12)-DAG(A12))&" dgn"

Put date in exell B12 and A12 and see result (ok)

Has anyone ever terminated the FW code to get the same result ?

Re: understanding OOP returning more than value from a method

Posted: Mon Oct 16, 2017 10:41 pm
by James Bott
Ehab,

First let me say that I would never recommend creating a Calculate() method (unless it was hidden). You don't want to have to call two methods to get the result.

When you request an age, that is what you should get. You should not have to remember to request a calculation first. The calculation should be done in the Age() method.

I have never needed a person's age in years, months and days so this question has never come up. A person's age in years, months, and days is a rare need. I would use three methods, Age(), AgeMonths(), and AgeDays(). So, when you request age() you get it in years as is normal. Otherwise, if you need months and days, also then you can request them too. All of these values would be returned as numbers.

And if you needed them in a string (to be displayed), then you could add another method AgeYMD() which returns the result as a string. This method would call the other three methods to get the needed numbers and paste them together in a string.

As others have mentioned, you do need to account for leap years in the calculations.

James

Re: understanding OOP returning more than value from a method

Posted: Mon Oct 16, 2017 11:08 pm
by Marc Venken
Hello James,

It was a very old post started by Ehab in 2010

I picked it up because I need the Year, Month and day for my soccer application.

As mentioned also, there is no need for OOP, because this can be done in a small function (like the exel sample)
Maybe someone has done it before. If not, I need to create it from that sample.

Thanks

Re: understanding OOP returning more than value from a method

Posted: Tue Oct 17, 2017 1:03 pm
by fafi
Hello,
Please test..

Code: Select all


#include "fivewin.ch"

function main()
local oPerson1,oPerson2

  SET _3DLOOK ON
  set date british
  set century on
  set delete on
  set confirm on
  SET EPOCH TO Year( Date() ) - 60
  
set date format to "DD/MM/YYYY"

oPerson1 :=Tperson():new("Enas Samir",ctod("09/05/1967"))

?alltrim(oPerson1:cName)+CRLF+;
        "DOB : "+dtoc(oPerson1:dDob)+CRLF+;
        "Today : "+dtoc(date())+CRLF+;
        str(oPerson1:nAge_years,3) +" years "+CRLF+;
        str(oPerson1:nAge_months,3)+" months "+CRLF+;
        str(oPerson1:nAge_days,3)  +" days "+CRLF


return nil


CLASS Tperson 
      data cName
      data dDob
      data nAge_years
      data nAge_months
      data nAge_days
      METHOD new(cName,dDob,nAge_years,nAge_months,nAge_days) CONSTRUCTOR 
      METHOD CalculateAge()
ENDCLASS

method new(cName,dDob,nAge_years,nAge_months,nAge_days) CLASS Tperson 
*-----------------------------------------------------
default cName:="",dDob:=ctod(""),nAge_years:=0,nAge_months:=0,nAge_days:=0
::cName:=cName
::dDob:=dDob
::nAge_years:=nAge_years
::nAge_months:=nAge_months
::nAge_days:=nAge_days
::CalculateAge()
return self

method CalculateAge() CLASS Tperson 

local nAge1 := 0
local nAge2 := 0
local nAge3 := 0
  
  if year(date()) <= year(::dDob)
     nAge1 := 0
  endif 
  
  if year(date()) > year(::dDob)
     nAge1 := year(date()) - year(::dDob)
  endif 
  
  if month(date()) == month(::dDob)
     nAge2 := 0
  endif 
  
  if month(date()) > month(::dDob)
     nAge2 := month(date()) - month(::dDob)
  endif 
  
  if month(date()) < month(::dDob)
     nAge2 := (12-month(::dDob))+ month(date())
     nAge1 -= 1
  endif 
  
  
  if day(date()) <= day(::dDob)
     nAge3 := 0
  endif
  
  if day(date()) > day(::dDob)
     nAge3 := day(date()) - day(::dDob)
  endif
  
  if ::dDob >= date()
     nAge1 := nAge2 := nAge3 := 0
  endif
  
      ::nAge_years:=nAge1
      ::nAge_months:=nAge2
      ::nAge_days:=nAge3

return nil   

 
regards
fafi

Re: understanding OOP returning more than value from a method

Posted: Tue Oct 17, 2017 1:13 pm
by karinha
Very good Fafi. Many thanks. Congratulations!
fafi wrote:Hello,
Please test..

Code: Select all


#include "fivewin.ch"

function main()
local oPerson1,oPerson2

  SET _3DLOOK ON
  set date british
  set century on
  set delete on
  set confirm on
  SET EPOCH TO Year( Date() ) - 60
  
set date format to "DD/MM/YYYY"

oPerson1 :=Tperson():new("Enas Samir",ctod("09/05/1967"))

?alltrim(oPerson1:cName)+CRLF+;
        "DOB : "+dtoc(oPerson1:dDob)+CRLF+;
        "Today : "+dtoc(date())+CRLF+;
        str(oPerson1:nAge_years,3) +" years "+CRLF+;
        str(oPerson1:nAge_months,3)+" months "+CRLF+;
        str(oPerson1:nAge_days,3)  +" days "+CRLF


return nil


CLASS Tperson 
      data cName
      data dDob
      data nAge_years
      data nAge_months
      data nAge_days
      METHOD new(cName,dDob,nAge_years,nAge_months,nAge_days) CONSTRUCTOR 
      METHOD CalculateAge()
ENDCLASS

method new(cName,dDob,nAge_years,nAge_months,nAge_days) CLASS Tperson 
*-----------------------------------------------------
default cName:="",dDob:=ctod(""),nAge_years:=0,nAge_months:=0,nAge_days:=0
::cName:=cName
::dDob:=dDob
::nAge_years:=nAge_years
::nAge_months:=nAge_months
::nAge_days:=nAge_days
::CalculateAge()
return self

method CalculateAge() CLASS Tperson 

local nAge1 := 0
local nAge2 := 0
local nAge3 := 0
  
  if year(date()) <= year(::dDob)
     nAge1 := 0
  endif 
  
  if year(date()) > year(::dDob)
     nAge1 := year(date()) - year(::dDob)
  endif 
  
  if month(date()) == month(::dDob)
     nAge2 := 0
  endif 
  
  if month(date()) > month(::dDob)
     nAge2 := month(date()) - month(::dDob)
  endif 
  
  if month(date()) < month(::dDob)
     nAge2 := (12-month(::dDob))+ month(date())
     nAge1 -= 1
  endif 
  
  
  if day(date()) <= day(::dDob)
     nAge3 := 0
  endif
  
  if day(date()) > day(::dDob)
     nAge3 := day(date()) - day(::dDob)
  endif
  
  if ::dDob >= date()
     nAge1 := nAge2 := nAge3 := 0
  endif
  
      ::nAge_years:=nAge1
      ::nAge_months:=nAge2
      ::nAge_days:=nAge3

return nil   

 
regards
fafi

Re: understanding OOP returning more than value from a method

Posted: Tue Oct 17, 2017 6:56 pm
by Marc Venken
For a date like : 26/11/1965

This has to change right ? Else the result for day = always 0

if day(date()) <= day(::dDob)
// nAge3 := 0
nAge3 := day(::dDob) - day(date())

endif

Re: understanding OOP returning more than value from a method

Posted: Tue Oct 17, 2017 7:22 pm
by fafi
Marc Venken wrote:For a date like : 26/11/1965

This has to change right ? Else the result for day = always 0

if day(date()) <= day(::dDob)
// nAge3 := 0
nAge3 := day(::dDob) - day(date())

endif
Thank you Mr. Marc

:D

oPerson1 :=Tperson():new("Marc Venken",ctod("26/11/1965"))

I think that is your DOB, right ? :lol:

Best Regards
oPerson1 :=Tperson():new("Fafi",ctod("09/05/1967"))

Re: understanding OOP returning more than value from a method

Posted: Wed Oct 18, 2017 12:08 am
by James Bott
Marc,

This is how do it using a class. Ideally, this should be modified to be a subclass of TRecord, then it will read it's own data from the DBF.

As you can see from the sample you just do:

Code: Select all

oPlayer:= TPlayer():new()

msgInfo( oPlayer:AgeYMD() )  // displays age in years, months, days
Two lines of code, that's all! A player should know his/her own age, so you should just be able to ask the player object for it.

Well, two lines to use the class.

Regards,
James

Code: Select all

/*
Purpose  : Player class
Program  : 
Author   : James Bott, jbott@compuserve.com
Date     : 10/17/2017 04:41:00 PM
Company  : Intellitech
Language : Fivewin/xHarbour
Updated  : 
Notes    : For Mark Venken
           Not fully tested, but seems to be working.
           Should be modifed to be a subclass of Intellitech's TRecord class,
           then it would read it's own data from the parent database.

*/

#include "fivewin.ch"

Function Main()
   Local oPlayer
   set date american  // Adjust to your local format
   set century on
   SET EPOCH TO 1980 

   oPlayer:= TPlayer():new()
   
   msgInfo( oPlayer:name, "oPlayer:name" )
   msgInfo( oPlayer:dob, "oPlayer:DOB" )
   msgInfo( oPlayer:age(), "oPlayer:age()" )
   msgInfo( oPlayer:AgeMonths(), "oPlayer:ageMonths()")
   MsgInfo( oPlayer:AgeDays(), "oPlayer:AgeDays()" )
   msgInfo( oPlayer:AgeYMD(), "oPlayer:AgeYMD()" )

Return nil

//----------------------------------------------------------------------------//

class TPlayer
   Var Name    
   Var Dob        
   Method New()
   Method Age()       // age in years
   Method AgeMonths()
   Method AgeDays()
   Method AgeYMD()    // age in string with years, months, days
endclass

//----------------------------------------------------------------------------//

Method New() Class TPlayer
   // These should be fields
   ::Name:= "Marc Venken"
   ::DOB := ctod("11/26/1965")
Return self

//----------------------------------------------------------------------------//

Method Age( dRefDate ) Class TPlayer
   
   LOCAL nAge := 0
   
   IF VALTYPE(dRefDate) <> "D"
      dRefDate := date()
   ENDIF

   IF ! EMPTY( ::DOB )

      nAge := YEAR(dRefDate) - YEAR( ::DOB )
      
      IF MONTH(dRefDate) < MONTH( ::DOB ) .OR. ;
         ( MONTH(dRefDate) == MONTH( ::DOB ) .AND. ;
           DAY(dRefDate) < DAY( ::DOB ) )
          nAge--
      ENDIF

   ENDIF

RETURN nAge

//----------------------------------------------------------------------------//

// Not fully tested
Method AgeDays() Class TPlayer
   Local nDays 
Return DOM( date() )

//----------------------------------------------------------------------------//

// Not fully tested
Method AgeMonths() Class TPlayer
   Local nMonths := 0
   if month(::DOB) > month( date() )
      nMonths := month( date() ) - month(::DOB) + 12 
   else
      nMonths:=  month( Date() ) - month( ::DOB ) - 1
   endif
Return nMonths

//----------------------------------------------------------------------------//

Method AgeYMD()
Return alltrim(str(::Age())) +" years, "+ alltrim(str(::AgeMonths())) + ;
   " months, " + allTrim(str(::AgeDays()))+" days" 

//----------------------------------------------------------------------------//

// EOF

Re: understanding OOP returning more than value from a method

Posted: Wed Oct 18, 2017 7:46 am
by Marc Venken
Thanks.

Will do some more testing with diff. dates, but seems pretty OK !

I just had to change

// Not fully tested
Method AgeDays() Class TPlayer
Local nDays
//Return DOM( date() )
Return day( date() )

DOM = a function of yours ? Is the change correct ?

Re: understanding OOP returning more than value from a method

Posted: Wed Oct 18, 2017 1:38 pm
by James Bott
Marc,

RE: DOM() and Day()

Sorry, yes, DOM stands for day-of-month and I wrote it many years ago. Day() returns the same number and it was originally a Clipper function, now a (x)Harbour function. I don't know if the day() function didn't exist when I wrote DOM(), or if I just didn't know it existed.

I hope you can now see how to move functions into Methods. This way they are encapsulated in the class. This makes them easier to find, and edit. And further, your code outside the class becomes much easier to read and understand. You also reduce a lot of variable passing, since you often use class data instead.

James