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