317 lines
7.9 KiB
AppleScript
317 lines
7.9 KiB
AppleScript
use AppleScript version "2.4"
|
|
use framework "Foundation"
|
|
use scripting additions
|
|
|
|
on run
|
|
unlines(map(¬
|
|
compose(compose(unlines, sparkLine), readFloats), ¬
|
|
{"0, 1, 19, 20", "0, 999, 4000, 4999, 7000, 7999", ¬
|
|
"0, 1000, 4000, 5000, 7000, 8000", ¬
|
|
"1 2 3 4 5 6 7 8 7 6 5 4 3 2 1", ¬
|
|
"1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"}))
|
|
end run
|
|
|
|
|
|
-- sparkLine :: [Float] -> [String]
|
|
on sparkLine(xs)
|
|
set ys to sort(xs)
|
|
set mn to item 1 of ys
|
|
set mx to item -1 of ys
|
|
set n to length of xs
|
|
set mid to (n div 2)
|
|
set w to (mx - mn) / 8
|
|
|
|
script bound
|
|
on |λ|(x)
|
|
mn + (w * x)
|
|
end |λ|
|
|
end script
|
|
set lbounds to map(bound, enumFromTo(1, 7))
|
|
|
|
script spark
|
|
on |λ|(x)
|
|
script flipGT
|
|
on |λ|(b)
|
|
b > x
|
|
end |λ|
|
|
end script
|
|
script indexedBlock
|
|
on |λ|(i)
|
|
item i of "▁▂▃▄▅▆▇"
|
|
end |λ|
|
|
end script
|
|
maybe("█", indexedBlock, findIndex(flipGT, lbounds))
|
|
end |λ|
|
|
end script
|
|
|
|
script str
|
|
on |λ|(x)
|
|
x as string
|
|
end |λ|
|
|
end script
|
|
|
|
{concat(map(spark, xs)), ¬
|
|
unwords(map(str, xs)), ¬
|
|
"Min " & mn as string, ¬
|
|
"Mean " & roundTo(mean(xs), 2) as string, ¬
|
|
"Median " & bool(item mid of xs, ((item mid of xs) + ¬
|
|
(item (mid + 1) of xs)) / 2, even(n)), ¬
|
|
"Max " & mx as string, ""}
|
|
end sparkLine
|
|
|
|
|
|
-- GENERIC -------------------------------------------------
|
|
|
|
-- Just :: a -> Maybe a
|
|
on Just(x)
|
|
{type:"Maybe", Nothing:false, Just:x}
|
|
end Just
|
|
|
|
-- Nothing :: Maybe a
|
|
on Nothing()
|
|
{type:"Maybe", Nothing:true}
|
|
end Nothing
|
|
|
|
-- bool :: a -> a -> Bool -> a
|
|
on bool(f, t, p)
|
|
if p then
|
|
t
|
|
else
|
|
f
|
|
end if
|
|
end bool
|
|
|
|
-- compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
|
on compose(f, g)
|
|
script
|
|
property mf : mReturn(f)
|
|
property mg : mReturn(g)
|
|
on |λ|(x)
|
|
mf's |λ|(mg's |λ|(x))
|
|
end |λ|
|
|
end script
|
|
end compose
|
|
|
|
-- concat :: [[a]] -> [a]
|
|
-- concat :: [String] -> String
|
|
on concat(xs)
|
|
set lng to length of xs
|
|
if 0 < lng and string is class of (item 1 of xs) then
|
|
set acc to ""
|
|
else
|
|
set acc to {}
|
|
end if
|
|
repeat with i from 1 to lng
|
|
set acc to acc & item i of xs
|
|
end repeat
|
|
acc
|
|
end concat
|
|
|
|
-- enumFromTo :: Int -> Int -> [Int]
|
|
on enumFromTo(m, n)
|
|
if m ≤ n then
|
|
set lst to {}
|
|
repeat with i from m to n
|
|
set end of lst to i
|
|
end repeat
|
|
return lst
|
|
else
|
|
return {}
|
|
end if
|
|
end enumFromTo
|
|
|
|
-- even :: Int -> Bool
|
|
on even(x)
|
|
0 = x mod 2
|
|
end even
|
|
|
|
-- Takes a predicate function and a list and
|
|
-- returns Just( the 1-based index of the first
|
|
-- element ) in the list satisfying the predicate
|
|
-- or Nothing if there is no such element.
|
|
-- findIndex(isSpace, "hello world")
|
|
--> {type:"Maybe", Nothing:false, Just:6}
|
|
|
|
-- findIndex(even, [3, 5, 7, 8, 9])
|
|
--> {type:"Maybe", Nothing:false, Just:4}
|
|
|
|
-- findIndex(isUpper, "all lower case")
|
|
--> {type:"Maybe", Nothing:true}
|
|
-- findIndex :: (a -> Bool) -> [a] -> Maybe Int
|
|
on findIndex(p, xs)
|
|
tell mReturn(p)
|
|
set lng to length of xs
|
|
repeat with i from 1 to lng
|
|
if |λ|(item i of xs) then return Just(i)
|
|
end repeat
|
|
return Nothing()
|
|
end tell
|
|
end findIndex
|
|
|
|
-- foldl :: (a -> b -> a) -> a -> [b] -> a
|
|
on foldl(f, startValue, xs)
|
|
tell mReturn(f)
|
|
set v to startValue
|
|
set lng to length of xs
|
|
repeat with i from 1 to lng
|
|
set v to |λ|(v, item i of xs, i, xs)
|
|
end repeat
|
|
return v
|
|
end tell
|
|
end foldl
|
|
|
|
-- map :: (a -> b) -> [a] -> [b]
|
|
on map(f, xs)
|
|
tell mReturn(f)
|
|
set lng to length of xs
|
|
set lst to {}
|
|
repeat with i from 1 to lng
|
|
set end of lst to |λ|(item i of xs, i, xs)
|
|
end repeat
|
|
return lst
|
|
end tell
|
|
end map
|
|
|
|
-- mean :: [Num] -> Num
|
|
on mean(xs)
|
|
script
|
|
on |λ|(a, x)
|
|
a + x
|
|
end |λ|
|
|
end script
|
|
foldl(result, 0, xs) / (length of xs)
|
|
end mean
|
|
|
|
-- | The 'maybe' function takes a default value, a function, and a 'Maybe'
|
|
-- value. If the 'Maybe' value is 'Nothing', the function returns the
|
|
-- default value. Otherwise, it applies the function to the value inside
|
|
-- the 'Just' and returns the result.
|
|
-- maybe :: b -> (a -> b) -> Maybe a -> b
|
|
on maybe(v, f, mb)
|
|
if Nothing of mb then
|
|
v
|
|
else
|
|
tell mReturn(f) to |λ|(Just of mb)
|
|
end if
|
|
end maybe
|
|
|
|
-- Lift 2nd class handler function into 1s class script wrapper
|
|
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
|
|
on mReturn(f)
|
|
if script is class of f then
|
|
f
|
|
else
|
|
script
|
|
property |λ| : f
|
|
end script
|
|
end if
|
|
end mReturn
|
|
|
|
-- readFloats :: String -> [Float]
|
|
on readFloats(s)
|
|
script asReal
|
|
on |λ|(n)
|
|
n as real
|
|
end |λ|
|
|
end script
|
|
map(asReal, splitRegex("[\\s,]+", s))
|
|
end readFloats
|
|
|
|
-- regexMatches :: String -> String -> [[String]]
|
|
on regexMatches(strRegex, strHay)
|
|
set ca to current application
|
|
-- NSNotFound handling and and High Sierra workaround due to @sl1974
|
|
set NSNotFound to a reference to 9.22337203685477E+18 + 5807
|
|
set oRgx to ca's NSRegularExpression's regularExpressionWithPattern:strRegex ¬
|
|
options:((ca's NSRegularExpressionAnchorsMatchLines as integer)) ¬
|
|
|error|:(missing value)
|
|
set oString to ca's NSString's stringWithString:strHay
|
|
|
|
script matchString
|
|
on |λ|(m)
|
|
script rangeMatched
|
|
on |λ|(i)
|
|
tell (m's rangeAtIndex:i)
|
|
set intFrom to its location
|
|
if NSNotFound ≠ intFrom then
|
|
text (intFrom + 1) thru (intFrom + (its |length|)) of strHay
|
|
else
|
|
missing value
|
|
end if
|
|
end tell
|
|
end |λ|
|
|
end script
|
|
end |λ|
|
|
end script
|
|
|
|
script asRange
|
|
on |λ|(x)
|
|
range() of x
|
|
end |λ|
|
|
end script
|
|
map(asRange, (oRgx's matchesInString:oString ¬
|
|
options:0 range:{location:0, |length|:oString's |length|()}) as list)
|
|
end regexMatches
|
|
|
|
|
|
-- roundTo :: Float -> Int -> Float
|
|
on roundTo(x, n)
|
|
set d to 10 ^ n
|
|
(round (x * d)) / d
|
|
end roundTo
|
|
|
|
-- sort :: Ord a => [a] -> [a]
|
|
on sort(xs)
|
|
((current application's NSArray's arrayWithArray:xs)'s ¬
|
|
sortedArrayUsingSelector:"compare:") as list
|
|
end sort
|
|
|
|
-- splitRegex :: Regex -> String -> [String]
|
|
on splitRegex(strRegex, str)
|
|
set lstMatches to regexMatches(strRegex, str)
|
|
if length of lstMatches > 0 then
|
|
script preceding
|
|
on |λ|(a, x)
|
|
set iFrom to start of a
|
|
set iLocn to (location of x)
|
|
|
|
if iLocn > iFrom then
|
|
set strPart to text (iFrom + 1) thru iLocn of str
|
|
else
|
|
set strPart to ""
|
|
end if
|
|
{parts:parts of a & strPart, start:iLocn + (length of x) - 1}
|
|
end |λ|
|
|
end script
|
|
|
|
set recLast to foldl(preceding, {parts:[], start:0}, lstMatches)
|
|
|
|
set iFinal to start of recLast
|
|
if iFinal < length of str then
|
|
parts of recLast & text (iFinal + 1) thru -1 of str
|
|
else
|
|
parts of recLast & ""
|
|
end if
|
|
else
|
|
{str}
|
|
end if
|
|
end splitRegex
|
|
|
|
-- unlines :: [String] -> String
|
|
on unlines(xs)
|
|
set {dlm, my text item delimiters} to ¬
|
|
{my text item delimiters, linefeed}
|
|
set str to xs as text
|
|
set my text item delimiters to dlm
|
|
str
|
|
end unlines
|
|
|
|
-- unwords :: [String] -> String
|
|
on unwords(xs)
|
|
set {dlm, my text item delimiters} to ¬
|
|
{my text item delimiters, space}
|
|
set s to xs as text
|
|
set my text item delimiters to dlm
|
|
return s
|
|
end unwords
|