Jump to content

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

Monster: evaluate math expressions in strings


  • Please log in to reply
87 replies to this topic
Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
If you want to evaluate a string, representing an arithmetic expression, you can use the Eval() function. Its parameter can be a literal string or a variable, containing a string, or even an AHK expression, resulting in a string of an arithmetic expression:
b := "a"

a := "1+2"

MsgBox % Eval(%b%)
The parameter of the Eval() function can contain other Eval() calls, so the recursion you are asking for is there. It just does not seem to be a good idea to generally change the meaning of %var% to Eval(%var%). It is an interesting idea you could gain some convenience with, but we loose consistency and possibilities.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
I have to read an arithmetic expression from Steam's skin definition file, replace some variables (which my function already does), and then evaluate the expression.

I was trying to start the daunting task of creating something like an Eval function for my SteamWin needs, when I found this.

Edit:

I initially posted to ask if I could use your functions from Monster in my script, but when looking further into them, I realized they are more generalized than I need. So as a result, I'm just going to use them as a reference in creating a lightweight eval function for simple math.

Thanks so much for this amazing script. I've learned a lot just from looking at it.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Here you find a much simpler version, which might be sufficient for your task. The following script is further simplified:
MsgBox % Eval("-2+(-1+2)*3")



Eval(x) {   ; Evaluate arithmetic expression with numbers, + - / * ( )

   Return Eval#(RegExReplace(x,"-","#")) ; # = subtraction, to distinguish from sign

}



Eval#(x) {  ; Evaluate expression with numbers, + #(subtract) / * ( ). Recurse into (..)

   Return RegExMatch(x,"(.*)\(([^\(\)]+)\)(.*)",y) ? Eval#(y1 . Eval@(y2) . y3) : Eval@(x)

}



Eval@(x) {  ; Evaluate expression with numbers, + #(subtract) / *

   RegExMatch(x,"(.*)(\+|#)(.*)",y)    ; last + or -

   IfEqual y2,+, Return Eval@(y1) + Eval@(y3)

   IfEqual y2,#, Return Eval@(y1) - Eval@(y3)



   RegExMatch(x,"(.*)(\*|/)(.*)",y)    ; last * or /

   IfEqual y2,*, Return Eval@(y1) * Eval@(y3)

   IfEqual y2,/, Return Eval@(y1) / Eval@(y3)



   Return x ? x : 0                    ; empty expression: 0, number: unchanged

}


bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Wow! I just finished writing a 51-line script to evaluate simple math expressions with parenthesis, and I feel completely and utterly humbled by the version you just posted. For some reason I didn't find the other script you linked to in my previous search, but I'm very glad for the new version you just posted above :)

It looks streamlined and lightweight, and it's perfect for my needs.

Thanks a million Laszlo!

greynite
  • Members
  • 40 posts
  • Last active: Jan 12 2011 03:28 PM
  • Joined: 17 May 2008
I chopped out the existing hotkeys, and am using the Eval() etc functions as a library. I then set up a hotkey for Cntl-C in my auto-start script, so that if hit twice quickly will Eval() the ClipBoard.

#include MathMONSTER (Core) by Laszlo.ahk

$^c::
    if gvCtrlCTicks
    {
        if (( A_TickCount - gvCtrlCTicks ) < 501 )
        {
            MonsterClip := ClipBoard
            if ( SubStr( MonsterClip, 0, 1) = "=" )
            {
                MonsterEval := Eval( SubStr(MonsterClip, 1, -1) )
                a := MonsterClip . MonsterEval
            }
            else
            {
                MonsterEval := Eval( MonsterClip )
                a := MonsterEval
            }
            ClipBoard := a
            caretx := A_CaretX
            carety := A_CaretY
            if ( carety > 150 )
                carety -= 25
            else
                carety += 20
            ToolTip, %a%,caretx,carety,2
            SetTimer, RemoveToolTipMonster, 1100
        }
        else
        {
            gvCtrlCTicks := A_TickCount
            Send ^c
        }
    }
    else
    {
        gvCtrlCTicks := A_TickCount
        Send ^c
    }
Return

RemoveToolTipMonster:
SetTimer, RemoveToolTipMonster, Off
ToolTip,,,,2
return

Thanks Laszlo for this great script!

Edit: I made a tweak in the Monster core, so instead of using "x" as a var prefix internally, it now uses "mmx":
If RegExMatch(x, "(\S*?)\s*:=\s*(.*)", y) {
      y := "x" . y1                    ; user vars internally start with x to avoid name conflicts
      Return %y% := Eval1(y2)
   }

   x := RegExReplace(x,"([a-z_A-Z]\w*)([^\w'»’]|$)","%x$1%$2") ; VAR -> %xVAR%
is now
If RegExMatch(x, "(\S*?)\s*:=\s*(.*)", y) {
      y := "mmx" . y1                    ; user vars internally start with x to avoid name conflicts
      Return %y% := Eval1(y2)
   }

   x := RegExReplace(x,"([a-z_A-Z]\w*)([^\w'»’]|$)","%mmx$1%$2") ; VAR -> %xVAR%
Enjoy,
Shawn

Chavez
  • Members
  • 256 posts
  • Last active: Oct 13 2009 01:27 PM
  • Joined: 20 Aug 2008
Seriously, Laszlo, you should try getting into Assembly, this stuff you do with AHK only already is amazing.
-Chavez.

greynite
  • Members
  • 40 posts
  • Last active: Jan 12 2011 03:28 PM
  • Joined: 17 May 2008

Seriously, Laszlo, you should try getting into Assembly, this stuff you do with AHK only already is amazing.


He already has :) He's got more than a few posts of binary ASM code to use in-line in AHK scripts to speed up various operations.

Matas
  • Members
  • 49 posts
  • Last active: Mar 12 2010 07:29 PM
  • Joined: 07 Mar 2009
Need help! I noticed that Sin(), Tan() and other trigonometry functions, is used with radians. How can I make it usable with degrees? This what i found in ahk help file:
Note: To convert a radians value to degrees, multiply it by 180/pi (approximately 57.29578). To convert a degrees value to radians, multiply it by pi/180 (approximately 0.01745329252). The value of pi (approximately 3.141592653589793) is 4 times the arctangent of 1.


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
deg := 57.295779513082321

MsgBox % sin(30/deg)   ; sine of 30 degrees

MsgBox % deg*asin(0.5) ; arc sine of 0.5 in degrees
In Monster you can use 180/pi in place of deg, that is:
sin(30/180*pi)

180/pi*asin(0.5)


Matas
  • Members
  • 49 posts
  • Last active: Mar 12 2010 07:29 PM
  • Joined: 07 Mar 2009
Thanks

Paddy3118
  • Members
  • 1 posts
  • Last active: Apr 23 2010 05:52 AM
  • Joined: 23 Apr 2010

Monster: evaluate math expressions in strings w/o external programs


Would you allow Rosetta code to show your code, under its license instead of its link here?

<!-- m -->http://rosettacode.o... ... AutoHotkey<!-- m -->

Thanks.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I have no objection, but Monster does not qualify:

an abstract-syntax tree (AST) for the expression must be created from parsing the input

.

flyingDman
  • Spam Officer
  • 2186 posts
  • Last active: Nov 07 2015 08:15 AM
  • Joined: 27 Feb 2009
Thanks Laszlo, very handy.
I was using the built-in constants and realized that you're using 1 gallon = 4.54609 liter (Imperial) rather than 3.785411784 liter (US)... :wink:

Marine Corps Gen. Joseph Dunford told senators at his Joint Chiefs of Staff confirmation hearing : “If you want to talk about a nation that could pose an existential threat to the United States, I'd have to point to Russia. And if you look at their behavior, it's nothing short of alarming.”


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

you're using 1 gallon = 4.54609 liter (Imperial) rather than 3.785411784 liter (US)

Thanks. I changed the first post to US gallon.

flyingDman
  • Spam Officer
  • 2186 posts
  • Last active: Nov 07 2015 08:15 AM
  • Joined: 27 Feb 2009
Why would this not give the correct result?:
MsgBox % Eval("$16 5.85")

It gives me 5.8499999999999996. Other examples show similar error.

:?:

Marine Corps Gen. Joseph Dunford told senators at his Joint Chiefs of Staff confirmation hearing : “If you want to talk about a nation that could pose an existential threat to the United States, I'd have to point to Russia. And if you look at their behavior, it's nothing short of alarming.”