RosettaCodeData/Task/Fibonacci-n-step-number-seq.../AppleScript/fibonacci-n-step-number-seq...

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