Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[func] calc() - math expression evaluation incl brackets


  • Please log in to reply
7 replies to this topic
derRaphael
  • Members
  • 872 posts
  • Last active: Mar 19 2013 04:42 PM
  • Joined: 23 Nov 2007
This very compacted function (its not perfect - i know) allows calculating expressions given by a string. it also supports the following functions: sqrt, log, ln, exp, sin, cos, tan, asin, acos, atan, rad, deg, abs, fib, gcb, min, max, sgn, **, ^, *, /, //, +, -, %, pi, e.

it may be extended more or less easily. its just another evaluating function. there are better alternatives (but also more complex) like the ones from laszlo (monster, eval, and friends).

basically i wrote this one to see how far an expession evaluating function can be compacted and still having a fair amount of functionality.

; dRs CALC! - http://www.opensource.org/licenses/mit-license.html
term1 := "10 * (10 + 12) / (5 * (8 + 2 * (1 + 3)) * (50 + 2))"
term2 := "1 * 2-1 * sin( cos(pi) )"
term3 := "sqrt(2^2/2)*(2**(1/2))"
term4 := "6 % 4"
term5 := "( 1 + 1 + 1 ) !"
term6 := "fib(6)"

msgbox % term1 " = " calc( term1 ) 
  . "`n" term2 " = " calc( term2 )
  . "`n" term3 " = " calc( term3 )
  . "`n" term4 " = " calc( term4 )
  . "`n" term5 " = " calc( term5 )
  . "`n" term6 " = " calc( term6 )
exitapp

calc( t, t0="", t1="", t2="" )
{   ; c-labeled functions by Laszlo: http://www.autohotkey.com/forum/topic17058.html
   static f := "sqrt|log|ln|exp|sin|cos|tan|asin|acos|atan|rad|deg|abs", c := "fib|gcb|min|max|sgn"
          o := "\*\*|\^|\*|/|//|\+|\-|%", pi:="pi", e:="e"
   if ( t0 = "fib"  && t1 != "" && t2 != "" ) {
      a := 0, b := 1 
      Loop % abs(t1)-1
         c := b, b += a, a := c
      return t1=0 ? 0 : t1>0 || t1&1 ? b : -b
   } else if ( t0 != "" && RegExMatch( t0, "(" f "|" c "|" o "|!)" ) && t1 != "" && t2 != "" )
      return t0 == "**" ? t1 ** t2 : t0 == "^" ? t1 ** t2 
           : t0 == "*" ? t1 * t2   : t0 == "/" ? t1 / t2 : t0 == "+" ? t1 + t2 : t0 == "-" ? t1 - t2
          : t0 == "//" ? t1 // t2 : t0 == "%" ? Mod( t1, t2 ) : t0 = "abs" ? abs( calc( t1 ) )
          : t0 == "!" ? ( t1 < 2 ? 1 : t1 * calc( t, t0, t1-1, 0 ) )
          : t0 = "log" ? log( calc( t1 ) ) : t0 = "ln" ? ln( calc ( t1 ) )
          : t0 = "sqrt" ? sqrt( calc( t1 ) ) : t0 = "exp" ? Exp( calc ( t1 ) )
          : t0 = "rad" ? calc( calc( t1 ) "* pi / 180" ) : t0 = "deg" ? calc( calc( t1 ) "* 180 / pi" )
          : t0 = "sin" ? sin( calc( "rad(" t1 ")" ) ) : t0 = "asin" ? asin( calc( "rad(" t1 ")" ) )
          : t0 = "cos" ? cos( calc( "rad(" t1 ")" ) ) : t0 = "acos" ? acos( calc( "rad(" t1 ")" ) )
          : t0 = "tan" ? tan( calc( "rad(" t1 ")" ) ) : t0 = "atan" ? atan( calc( "rad(" t1 ")" ) )
          : t0 = "min" ? ( t1 < t2 ? t1 : t2 ) : t0 = "max" ? ( t1 < t2 ? t2 : t1 )
          : t0 = "gcd" ? ( t2 = 0 ? abs(t1) : calc( t, t0, calc( t1 "%" t2 ) ) )
          : t0 = "sgn" ? (t1>0)-(t1<0) : 0
   
   t := RegExReplace( t, "\s*", "" )
   while ( RegExMatch( t, "i)" f "|" c "|" o "|" pi "|" e "|!" ) )
      if ( RegExMatch( t, "i)\b" pi "\b" ) )
         t := RegExReplace( t, "i)\b" pi "\b", 4 * atan(1) )
      else if ( RegExMatch( t, "i)\b" e "\b" ) )
         t := RegExReplace( t, "i)\b" e "\b", 2.718281828459045 )
      else if ( RegExMatch( t, "i)(" f "|" c ").*", s ) 
             && RegExMatch( s, "(?>[^\(\)]*)\((?>[^\(\)]*)(?R)*(?>[^\(\)]*)\)", m )
            && RegExMatch( m, "(?P<0>[^(]+)\((?P<1>[^,]*)(,(?P<2>.*))?\)", p ) )
         t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 != "" ? p2 : 0 ) )
      else if ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)!", p) )
         t := RegExReplace( t, "\Q" p "\E", calc( "", "!", p1, 0 ) )
      else if ( RegExMatch( t, "\((?P<0>[^(]+)\)", p ) )
         t := RegExReplace( t, "\Q(" p0 ")\E", calc( p0 ) )
      else
         loop, parse, o, |
            while ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)(?P<0>" A_LoopField ")(?P<2>-*\d+(\.\d+)?)", p ) )
               t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 ) )
   return t
}

esc::exitapp

greets
dR

All scripts, unless otherwise noted, are hereby released under CC-BY

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Amazing! This seems like a very good alternative when Monster is too big and complicated.

Just one question: is there an alternative to recursion? For example, with a loop and a pseudo-stack?

derRaphael
  • Members
  • 872 posts
  • Last active: Mar 19 2013 04:42 PM
  • Joined: 23 Nov 2007
at the current stage of development - the function is designed to work only due to recursion. probably it might be redesigned in a manner to use internal jumplabels thus avoiding the recursions. thx for the interest.

greets
dR

All scripts, unless otherwise noted, are hereby released under CC-BY

Smurth
  • Members
  • 120 posts
  • Last active: Feb 23 2014 09:58 PM
  • Joined: 13 Dec 2006
Hi,

Great script; exactly what I need...
But I've found one bug: when I try to call calc with something like cos(90), cos(179), etc., AutoHotkey goes in an endless loop :(

I've tried with AutoHotkey and AHK_L, under XP and Win7. Still the same.

robertcollier4
  • Members
  • 141 posts
  • Last active: Jun 29 2017 12:42 PM
  • Joined: 11 Nov 2012

But I've found one bug: when I try to call calc with something like cos(90), cos(179), etc., AutoHotkey goes in an endless loop sad.png

I've tried with AutoHotkey and AHK_L, under XP and Win7. Still the same.

 
Found the problem, here's the fix. Find the line:
: t0 = "rad" ? calc( calc( t1 ) "* pi / 180" ) : t0 = "deg" ? calc( calc( t1 ) "* 180 / pi" )
 change to:
: t0 = "rad" ? calc( t1 )*4*atan(1)/180 : t0 = "deg" ? calc( t1 )*180/4*atan(1)


Sam.
  • Members
  • 107 posts
  • Last active: Jul 15 2017 04:06 PM
  • Joined: 14 Nov 2008

Very useful function, thank you!

 

However, I have found that running

equation=-3+1
MsgBox % calc(equation)
ExitApp

seems to result in an infinite loop.



robertcollier4
  • Members
  • 141 posts
  • Last active: Jun 29 2017 12:42 PM
  • Joined: 11 Nov 2012
Seems to infinite loop on any negative number.
MsgBox % calc(-5)
Something to do with the regex parsing for negative numbers, I tried looking into it but I don't understand some of those Regex tags. The infinite loop seems to occur in the following piece of code.
      else
         loop, parse, o, |
            while ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)(?P<0>" A_LoopField ")(?P<2>-*\d+(\.\d+)?)", p ) )
               t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 ) )
Anyone understand whats going on? This is my favorite calc function since looking for something minimalistic.

Lv2
  • Members
  • 23 posts
  • Last active: Dec 18 2014 05:05 PM
  • Joined: 10 Jun 2014

I don't mean to resurrect an old thread but I happened across this and thought I'd contribute.

 

The problem stems (I believe) from the regex thinking negative numbers are equations because they contain the character "-".

It's not ideal but I believe you can use the following to terminate the loop and return negative values.

; dRs CALC! - http://www.opensource.org/licenses/mit-license.html
  
Msgbox % Calc("-3+1")

exitapp
return

Esc::ExitApp

calc( t, t0="", t1="", t2="" )
{   ; c-labeled functions by Laszlo: http://www.autohotkey.com/forum/topic17058.html
   static f := "sqrt|log|ln|exp|sin|cos|tan|asin|acos|atan|rad|deg|abs", c := "fib|gcb|min|max|sgn"
          o := "\*\*|\^|\*|/|//|\+|\-|%", pi:="pi", e:="e"
   if ( t0 = "fib"  && t1 != "" && t2 != "" ) {
      a := 0, b := 1 
      Loop % abs(t1)-1
         c := b, b += a, a := c
      return t1=0 ? 0 : t1>0 || t1&1 ? b : -b
   } else if ( t0 != "" && RegExMatch( t0, "(" f "|" c "|" o "|!)" ) && t1 != "" && t2 != "" )
      return t0 == "**" ? t1 ** t2 : t0 == "^" ? t1 ** t2 
           : t0 == "*" ? t1 * t2   : t0 == "/" ? t1 / t2 : t0 == "+" ? t1 + t2 : t0 == "-" ? t1 - t2
          : t0 == "//" ? t1 // t2 : t0 == "%" ? Mod( t1, t2 ) : t0 = "abs" ? abs( calc( t1 ) )
          : t0 == "!" ? ( t1 < 2 ? 1 : t1 * calc( t, t0, t1-1, 0 ) )
          : t0 = "log" ? log( calc( t1 ) ) : t0 = "ln" ? ln( calc ( t1 ) )
          : t0 = "sqrt" ? sqrt( calc( t1 ) ) : t0 = "exp" ? Exp( calc ( t1 ) )
          : t0 = "rad" ? calc( t1 )*4*atan(1)/180 : t0 = "deg" ? calc( t1 )*180/4*atan(1)
          : t0 = "sin" ? sin( calc( "rad(" t1 ")" ) ) : t0 = "asin" ? asin( calc( "rad(" t1 ")" ) )
          : t0 = "cos" ? cos( calc( "rad(" t1 ")" ) ) : t0 = "acos" ? acos( calc( "rad(" t1 ")" ) )
          : t0 = "tan" ? tan( calc( "rad(" t1 ")" ) ) : t0 = "atan" ? atan( calc( "rad(" t1 ")" ) )
          : t0 = "min" ? ( t1 < t2 ? t1 : t2 ) : t0 = "max" ? ( t1 < t2 ? t2 : t1 )
          : t0 = "gcd" ? ( t2 = 0 ? abs(t1) : calc( t, t0, calc( t1 "%" t2 ) ) )
          : t0 = "sgn" ? (t1>0)-(t1<0) : 0
   
   t := RegExReplace( t, "\s*", "" )
   while ( RegExMatch( t, "i)" f "|" c "|" o "|" pi "|" e "|!" ) )
      if ( RegExMatch( t, "i)\b" pi "\b" ) )
         t := RegExReplace( t, "i)\b" pi "\b", 4 * atan(1) )
      else if ( RegExMatch( t, "i)\b" e "\b" ) )
         t := RegExReplace( t, "i)\b" e "\b", 2.718281828459045 )
      else if ( RegExMatch( t, "i)(" f "|" c ").*", s ) 
             && RegExMatch( s, "(?>[^\(\)]*)\((?>[^\(\)]*)(?R)*(?>[^\(\)]*)\)", m )
            && RegExMatch( m, "(?P<0>[^(]+)\((?P<1>[^,]*)(,(?P<2>.*))?\)", p ) )
         t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 != "" ? p2 : 0 ) )
      else if ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)!", p) )
         t := RegExReplace( t, "\Q" p "\E", calc( "", "!", p1, 0 ) )
      else if ( RegExMatch( t, "\((?P<0>[^(]+)\)", p ) )
         t := RegExReplace( t, "\Q(" p0 ")\E", calc( p0 ) )
      else
		loop, parse, o, |
			{
				while ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)(?P<0>" A_LoopField ")(?P<2>-*\d+(\.\d+)?)", p ) )
					t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 ) )
				If ( RegExMatch(SubStr(t, 2), "[(\*\*)\^\*(//)/\+\-`%]") = 0 )
					return t
			}
   return t
}