Can’t find any language for this.
If you have Excel, you could use that function or you could use the series expansion to calculate it.
on Sin(x)
set maxLoop to 19
set factorial to 1
set retVal to 0
repeat with i from 1 to maxLoop by 2
set term to (-1 ^ ((i mod 4 - 1) / 2)) * (x ^ i) / factorial
set retVal to retVal + term
set factorial to factorial * (i + 1) * (i + 2)
end repeat
return retVal
end Sin
on xlSin(x)
tell application "Microsoft Excel"
return (execute excel 4 macro VB macro name "SIN(" & x & ")")
end tell
end xlSin
Cos x = 1 - x^2/2 + x^4/4! - x^6/6! + …
JavaScript is supposed to be very good at this kind of Math (Im not BTW much has been forgotten since school) so you could always have Safari help you out doing the math by doing its JavaScript and passing you back the result (I only say this as you should have this app installed there are other apps that can do JavaScript too so check) This should give you some idea anyhow.
set JS to "function getAngle() {
// Should have right angle triangle
var adjSide = 10;
var oppSide = 16;
var myAngle = (Math.atan(oppSide / adjSide)) * (180 / Math.PI);
return myAngle;
};
getAngle();"
tell application "Safari"
activate
set MyMath to do JavaScript JS in front document
display dialog the result
end tell
Hi,
…or simply using awk’s arithmetic built-in functions
-- - available functions
-- - cos(x); exp(ex); int(x); log(x) ; sin(x); sqrt(x); atan2(y,x); rand(); srand(x)
set fct to "cos"
set angle to 30 -- in degree
set x to (do shell script "awk 'BEGIN{ pi = 4.0*atan2(1.0,1.0); degree = pi/180.0; print " & fct & "(" & angle & "*degree)}'") as real
if you need precision use the printf statement:
set fct to "cos"
set angle to 30 -- in degree
set precision to 12 -- interger
set x to (do shell script "awk 'BEGIN{ pi = 4.0*atan2(1.0,1.0); degree = pi/180.0; calc=" & fct & "(" & angle & "*degree); printf(\"%." & precision & "f\\n\", calc)}'") as real
and to calculate the tangent:
set angle to 30 -- in degree
set precision to 12 -- interger
set |tan| to do shell script "awk 'BEGIN{
pi = 4.0*atan2(1.0,1.0)
degree = pi/180.0
sinus = sin(" & angle & " * degree)
cosinus = cos(" & angle & " * degree)
print sinus/cosinus
}'"
you could also use the bc’s math library:
-- s (x) -- sine of x, x in radians.
-- c (x) -- cosine of x, x in radians.
-- a (x) -- arctangent of x, arctangent returns radians.
-- l (x) -- natural logarithm of x.
-- e (x) -- exponential function of raising e to the value x.
-- j (n,x) -- bessel function of integer order n of x.
set |angle°| to 60
set fct to "c"
set precision to 13 -- interger
set y to do shell script "echo \"scale=" & precision & "; " & fct & "(" & |angle°| & " * (4 * a(1) / 180))\" | bc -l"
hth
Or simply (a bit more concise version of Mark67’s answer):
on math(f, x)
return run script "Math." & f & "(" & x & ")" in "JavaScript"
end math
on sin(x)
math("sin", x)
end sin
on cos(x)
math("cos", x)
end cos
on random()
math("random", "")
end random
log "sin(pi as real / 6): " & sin((pi as real) / 6) (*sin(pi as real / 6): 0.5*)
log "cos(pi as real / 6): " & cos((pi as real) / 6) (*cos(pi as real / 6): 0.866025403785*)
log "random(): " & random() (*random(): 0.687339054003*)
And similarly for abs, acos, acosh, asin, asinh, atan, atanh, cbrt, ceil, clz32, cosh, exp, expm1, floor, fround, log, log1p, log10, log2, round, sign, sin, sinh, sqrt, tan, tanh, and trunc.
As atan2, imul, and pow need 2 parameters, we could have some math2(f, x, y) method.
As hypot, max, and min can have many parameters in JavaScript, we need a mathN(f, list_), too, and listToString() in order to concatenate the list using “,”:
on listToString(list_)
set AppleScript's text item delimiters to ", "
set string_ to list_ as text
set AppleScript's text item delimiters to {""} --> restore delimiters to default
string_
end listToString
on mathN(f, list_)
run script "Math." & f & "(" & parameters & ")" in "JavaScript"
end mathN
on max(list_)
mathN("max", list_)
end max
on min(list_)
mathN("min", list_)
end min
log "max({5,9,3,10,4}): " & max({5, 9, 3, 10, 4}) (*max({5,9,3,10,4}): 10*)
log "min({5,9,3,10,4}): " & min({5, 9, 3, 10, 4}) (*min({5,9,3,10,4}): 3*)
(Note that mathN could be used for the methods with just one parameter and of course also for those that need two, e.g., mathN(“sin”, {x}) and mathN(“pow”, {x, y}…)
There are some other cool possibilities of integrating JavaScript within AppleScript:
- Sorting a list is certainly quite useful and while for instance the simple_sort at https://www.macosxautomation.com/applescript/sbrt/sbrt-05.html has ~20 lines in pure AppleScript, with JavaScript it can be done on a single line in pretty straightforward way:
on sort(list_)
run script "[" & listToString(list_) & "].sort((n1, n2) => n1 - n2)" in "JavaScript"
end sort
log "sort({5, 9, 3, 10, 4}): " & listToString(sort({5, 9, 3, 10, 4})) (*sort({5, 9, 3, 10, 4}): 3, 4, 5, 9, 10*)
- Let’s now be a bit more adventurous and generate an array of randomly shuffled numbers:
on shuffle(n)
run script "[...Array(" & n & ").keys()].map((i)=>({at:Math.random(),value:i+1})).sort((x,y)=>x.at-y.at).map((x)=>x.value)" in "JavaScript"
end shuffle
log "shuffle(10): " & listToString(shuffle(10)) (*shuffle(10): 9, 6, 4, 1, 2, 5, 7, 3, 10, 8*)
Another ±1-liner! But the main point is that we can use just a single expression in the new JavaScript ES6 that now supports => arrow functions, the ‘…’ spread operator and plenty of good array functions - such as keys(), map(), and again sort() - which can greatly simplify our AppleScreepy lifes. And thankfully JavaScript ES6 is now supported by AppleScript!
PS 1. Isn’t it kinda funny that we use sorting to shuffle a list? (The algorithm is cool, it first creates an array without values, then fills it with the indices using ‘…’ and key(). Using the first map() it then fills the array with objects with a random position and a value derived from each index. These objects can then be sorted according to their random position and then the second map() just extracts the values.)
PS 2. I have to admit that in order to put together shuffle() I had to steal the main components from two StackOverflow wizzards: Ben in Does JavaScript have a method like “range()” to generate a range within the supplied bounds? taught me to use ‘…’ and key() and superluminary in How to randomize (shuffle) a JavaScript array? baffled me with the really ingenious way how to ‘randomly sort’ an array. (Note that this implementation is a bit slow not only because all the map() and key() calls traverse the entire array but more importantly because sort() can be the big O(n^2) deal while other shuffle algorithms need just O(n) - so use only for small arrays)
Caveat:
Another interesting possibility is to use JavaScript’s String functions that allow for RegExp (regular expressions) to simplify coding in AppleScript:
# Returns 'duality' surrounded by single quotes if it's a string, or untouched if it's a RegExp
on quotedIfString(duality)
if first character of duality is "/" then return duality -- a RegExp, just return it
return quoted form of duality -- it's a string, return it in quotes
end quotedIfString
on match(inString, regExp)
run script "'" & inString & "'.match(" & regExp & ")" in "JavaScript"
end match
on replace(inString, duality, byString)
run script "'" & inString & "'.replace(" & quotedIfString(duality) & ",'" & byString & "')" in "JavaScript"
end replace
on split(text_, duality)
run script "'" & text_ & "'.split(" & quotedIfString(duality) & ")" in "JavaScript"
end split
on niceListString(list_)
"[{" & replace(listToString(list_), "/, /g", "}, {") & "}]"
end niceListString
set niceList to niceListString(match("/Users/jan/.atom/styles.less", "/(.*\\/)(.+?)\\.(\\w+)$/"))
log "[{all, folder, name, extension}]: " & niceList
(*{all, folder, name, extension}: [{/Users/jan/.atom/styles.less}, {/Users/jan/.atom/}, {styles}, {less}]*)
log "extension: " & item 5 of split(niceList, "/[\\[{, }\\]]{2,4}/") (*extension: less*)
log "extension: " & item 4 of split(replace(niceList, "/\\[{|}\\]/g", ""), "}, {") (*extension: less*)
log "extension: " & item 4 of (run script "'" & niceList & "'.replace('[{', '').replace('}]', '').split('}, {')" in "JavaScript") (*extension: less*)
Note 1: '' in a RegExp must be doubled as ‘\’.
Note 2: Obviously an ‘extension’ could be easily obtained as item 4 in the match(). But the contrived ways using the complicated niceList demonstrate nicely how useful are JavaScript’s split() and replace() with and/or without a RegExp
…And of course how useful it can be to integrate JavaScript’s features in AppleScript.
PS 3. Maybe this post could/should go to a separate category given it’s generic and not just about Math?
Here are some AppleScript routines to calculate Sine, Cosine, & Tangent
These are from a library I downloaded years ago. Not sure where.
on sine(x)
local answer, numerator, denominator, factor
if x ≥ 0 then
set x to x mod 360
else
set x to 360 + (x mod 360)
end if
display alert x
--convert from degrees to radians
set x to x * (2 * pi) / 360
set answer to 0
set numerator to x
set denominator to 1
set factor to -(x ^ 2)
repeat with i from 3 to 40 by 2
set answer to answer + numerator / denominator
set numerator to numerator * factor
set denominator to denominator * i * (i - 1)
end repeat
return answer
end sine
on cosine(x)
local answer, numerator, denominator, factor
if x ≥ 0 then
set x to x mod 360
else
set x to 360 + (x mod 360)
end if
--convert from degrees to radians
set x to x * (2 * pi) / 360
set answer to 0
set numerator to 1
set denominator to 1
set factor to -(x ^ 2)
repeat with i from 2 to 40 by 2
set answer to answer + numerator / denominator
set numerator to numerator * factor
set denominator to denominator * i * (i - 1)
end repeat
return answer
end cosine
--x is in degrees
on tan(x)
local answer
set answer to sine(x) / (cosine(x))
return answer
end tan
on tangent(x)
local factor, sanswer, canswer, snum, cnum, sdenom, cdenom
if x ≥ 0 then
set x to x mod 360
else
set x to 360 + (x mod 360)
end if
--convert from degrees to radians
set x to x * (2 * pi) / 360
set {sanswer, canswer} to {0, 0}
set {cnum, snum} to {1, x}
set {cdenom, sdenom} to {1, 1}
set factor to -(x ^ 2)
repeat with i from 2 to 40 by 2
set canswer to canswer + cnum / cdenom
set sanswer to sanswer + snum / sdenom --(cdenom * (i + 1))
set cnum to cnum * factor
set snum to snum * factor
set cdenom to cdenom * i * (i - 1)
set sdenom to cdenom * (i + 1)
end repeat
return sanswer / canswer
end tangent
You’ll notice there are 2 versions on Tangent