245 lines
5.5 KiB
AppleScript
245 lines
5.5 KiB
AppleScript
use AppleScript version "2.4"
|
|
use framework "Foundation"
|
|
use scripting additions
|
|
|
|
|
|
-- Start sequence -> Number of terms -> terms
|
|
-- takeNFibs :: [Int] -> Int -> [Int]
|
|
on takeNFibs(xs, n)
|
|
script go
|
|
on |λ|(xs, n)
|
|
if 0 < n and 0 < length of xs then
|
|
cons(head(xs), ¬
|
|
|λ|(append(tail(xs), {sum(xs)}), n - 1))
|
|
else
|
|
{}
|
|
end if
|
|
end |λ|
|
|
end script
|
|
go's |λ|(xs, n)
|
|
end takeNFibs
|
|
|
|
-- fibInit :: Int -> [Int]
|
|
on fibInit(n)
|
|
script powerOfTwo
|
|
on |λ|(x)
|
|
2 ^ x as integer
|
|
end |λ|
|
|
end script
|
|
cons(1, map(powerOfTwo, enumFromToInt(0, n - 2)))
|
|
end fibInit
|
|
|
|
-- TEST ---------------------------------------------------
|
|
on run
|
|
set intTerms to 15
|
|
script series
|
|
on |λ|(s, n)
|
|
justifyLeft(12, space, s & "nacci") & " -> " & ¬
|
|
showJSON(takeNFibs(fibInit(n), intTerms))
|
|
end |λ|
|
|
end script
|
|
|
|
set strTable to unlines(zipWith(series, ¬
|
|
words of ("fibo tribo tetra penta hexa hepta octo nona deca"), ¬
|
|
enumFromToInt(2, 10)))
|
|
|
|
justifyLeft(12, space, "Lucas ") & " -> " & ¬
|
|
showJSON(takeNFibs({2, 1}, intTerms)) & linefeed & strTable
|
|
end run
|
|
|
|
-- GENERIC FUNCTIONS --------------------------------------
|
|
|
|
-- Append two lists.
|
|
-- append (++) :: [a] -> [a] -> [a]
|
|
-- append (++) :: String -> String -> String
|
|
on append(xs, ys)
|
|
xs & ys
|
|
end append
|
|
|
|
-- cons :: a -> [a] -> [a]
|
|
on cons(x, xs)
|
|
if list is class of xs then
|
|
{x} & xs
|
|
else
|
|
x & xs
|
|
end if
|
|
end cons
|
|
|
|
-- enumFromToInt :: Int -> Int -> [Int]
|
|
on enumFromToInt(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 enumFromToInt
|
|
|
|
-- 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
|
|
|
|
-- head :: [a] -> a
|
|
on head(xs)
|
|
if xs = {} then
|
|
missing value
|
|
else
|
|
item 1 of xs
|
|
end if
|
|
end head
|
|
|
|
-- justifyLeft :: Int -> Char -> String -> String
|
|
on justifyLeft(n, cFiller, strText)
|
|
if n > length of strText then
|
|
text 1 thru n of (strText & replicate(n, cFiller))
|
|
else
|
|
strText
|
|
end if
|
|
end justifyLeft
|
|
|
|
-- Lift 2nd class handler function into 1st class script wrapper
|
|
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
|
|
on mReturn(f)
|
|
if class of f is script then
|
|
f
|
|
else
|
|
script
|
|
property |λ| : f
|
|
end script
|
|
end if
|
|
end mReturn
|
|
|
|
-- 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
|
|
|
|
-- min :: Ord a => a -> a -> a
|
|
on min(x, y)
|
|
if y < x then
|
|
y
|
|
else
|
|
x
|
|
end if
|
|
end min
|
|
|
|
-- Egyptian multiplication - progressively doubling a list, appending
|
|
-- stages of doubling to an accumulator where needed for binary
|
|
-- assembly of a target length
|
|
-- replicate :: Int -> a -> [a]
|
|
on replicate(n, a)
|
|
set out to {}
|
|
if n < 1 then return out
|
|
set dbl to {a}
|
|
|
|
repeat while (n > 1)
|
|
if (n mod 2) > 0 then set out to out & dbl
|
|
set n to (n div 2)
|
|
set dbl to (dbl & dbl)
|
|
end repeat
|
|
return out & dbl
|
|
end replicate
|
|
|
|
-- showJSON :: a -> String
|
|
on showJSON(x)
|
|
set c to class of x
|
|
if (c is list) or (c is record) then
|
|
set ca to current application
|
|
set {json, e} to ca's NSJSONSerialization's ¬
|
|
dataWithJSONObject:x options:0 |error|:(reference)
|
|
if json is missing value then
|
|
e's localizedDescription() as text
|
|
else
|
|
(ca's NSString's alloc()'s ¬
|
|
initWithData:json encoding:(ca's NSUTF8StringEncoding)) as text
|
|
end if
|
|
else if c is date then
|
|
"\"" & ((x - (time to GMT)) as «class isot» as string) & ".000Z" & "\""
|
|
else if c is text then
|
|
"\"" & x & "\""
|
|
else if (c is integer or c is real) then
|
|
x as text
|
|
else if c is class then
|
|
"null"
|
|
else
|
|
try
|
|
x as text
|
|
on error
|
|
("«" & c as text) & "»"
|
|
end try
|
|
end if
|
|
end showJSON
|
|
|
|
-- sum :: [Num] -> Num
|
|
on sum(xs)
|
|
script add
|
|
on |λ|(a, b)
|
|
a + b
|
|
end |λ|
|
|
end script
|
|
|
|
foldl(add, 0, xs)
|
|
end sum
|
|
|
|
-- tail :: [a] -> [a]
|
|
on tail(xs)
|
|
set blnText to text is class of xs
|
|
if blnText then
|
|
set unit to ""
|
|
else
|
|
set unit to {}
|
|
end if
|
|
set lng to length of xs
|
|
if 1 > lng then
|
|
missing value
|
|
else if 2 > lng then
|
|
unit
|
|
else
|
|
if blnText then
|
|
text 2 thru -1 of xs
|
|
else
|
|
rest of xs
|
|
end if
|
|
end if
|
|
end tail
|
|
|
|
-- 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
|
|
|
|
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
|
|
on zipWith(f, xs, ys)
|
|
set lng to min(length of xs, length of ys)
|
|
if 1 > lng then return {}
|
|
set lst to {}
|
|
tell mReturn(f)
|
|
repeat with i from 1 to lng
|
|
set end of lst to |λ|(item i of xs, item i of ys)
|
|
end repeat
|
|
return lst
|
|
end tell
|
|
end zipWith
|