How to resolve the algorithm Permutations step by step in the AppleScript programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Permutations step by step in the AppleScript programming language

Table of Contents

Problem Statement

Write a program that generates all   permutations   of   n   different objects.   (Practically numerals!)

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Permutations step by step in the AppleScript programming language

Source code in the applescript programming language

----------------------- PERMUTATIONS -----------------------

-- permutations :: [a] -> [[a]]
on permutations(xs)
    script go
        on |λ|(xs)
            script h
                on |λ|(x)
                    script ts
                        on |λ|(ys)
                            {{x} & ys}
                        end |λ|
                    end script
                    concatMap(ts, go's |λ|(|delete|(x, xs)))
                end |λ|
            end script
            
            if {}  xs then
                concatMap(h, xs)
            else
                {{}}
            end if
        end |λ|
    end script
    go's |λ|(xs)
end permutations


--------------------------- TEST ---------------------------
on run
    
    permutations({"aardvarks", "eat", "ants"})
    
end run


-------------------- GENERIC FUNCTIONS ---------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lst to {}
    set lng to length of xs
    tell mReturn(f)
        repeat with i from 1 to lng
            set lst to (lst & |λ|(contents of item i of xs, i, xs))
        end repeat
    end tell
    return lst
end concatMap


-- delete :: a -> [a] -> [a]
on |delete|(x, xs)
    if length of xs > 0 then
        set {h, t} to uncons(xs)
        if x = h then
            t
        else
            {h} & |delete|(x, t)
        end if
    else
        {}
    end if
end |delete|


-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn


-- uncons :: [a] -> Maybe (a, [a])
on uncons(xs)
    if length of xs > 0 then
        {item 1 of xs, rest of xs}
    else
        missing value
    end if
end uncons


to DoPermutations(aList, n)
    --> Heaps's algorithm (Permutation by interchanging pairs)
    if n = 1 then
        tell (a reference to PermList) to copy aList to its end
        -- or: copy aList as text (for concatenated results)
    else
        repeat with i from 1 to n
            DoPermutations(aList, n - 1)
            if n mod 2 = 0 then -- n is even
                tell aList to set [item i, item n] to [item n, item i] -- swaps items i and n of aList
            else
                tell aList to set [item 1, item n] to [item n, item 1] -- swaps items 1 and n of aList
            end if
        end repeat
    end if
    return (a reference to PermList) as list
end DoPermutations

--> Example 1 (list of words)
set [SourceList, PermList] to [{"Good", "Johnny", "Be"}, {}]
DoPermutations(SourceList, SourceList's length)
--> result (value of PermList)
{{"Good", "Johnny", "Be"}, {"Johnny", "Good", "Be"}, {"Be", "Good", "Johnny"}, ¬
    {"Good", "Be", "Johnny"}, {"Johnny", "Be", "Good"}, {"Be", "Johnny", "Good"}}

--> Example 2 (characters with concatenated results)
set [SourceList, PermList] to [{"X", "Y", "Z"}, {}]
DoPermutations(SourceList, SourceList's length)
--> result (value of PermList)
{"XYZ", "YXZ", "ZXY", "XZY", "YZX", "ZYX"}

--> Example 3 (Integers)
set [SourceList, Permlist] to [{1, 2, 3}, {}]
DoPermutations(SourceList, SourceList's length)
--> result (value of Permlist)
{{1, 2, 3}, {2, 1, 3}, {3, 1, 2}, {1, 3, 2}, {2, 3, 1}, {3, 2, 1}}

--> Example 4 (Integers with concatenated results)
set [SourceList, Permlist] to [{1, 2, 3}, {}]
DoPermutations(SourceList, SourceList's length)
--> result (value of Permlist)
{"123", "213", "312", "132", "231", "321"}


----------------------- PERMUTATIONS -----------------------

-- permutations :: [a] -> [[a]]
on permutations(xs)
    script go
        on |λ|(x, a)
            script
                on |λ|(ys)
                    script infix
                        on |λ|(n)
                            if ys  {} then
                                take(n, ys) & {x} & drop(n, ys)
                            else
                                {x}
                            end if
                        end |λ|
                    end script
                    map(infix, enumFromTo(0, (length of ys)))
                end |λ|
            end script
            concatMap(result, a)
        end |λ|
    end script
    foldr(go, {{}}, xs)
end permutations


--------------------------- TEST ---------------------------
on run
    
    permutations({1, 2, 3})
    
    --> {{1, 2, 3}, {2, 1, 3}, {2, 3, 1}, {1, 3, 2}, {3, 1, 2}, {3, 2, 1}}
end run


------------------------- GENERIC --------------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & |λ|(item i of xs, i, xs)
        end repeat
    end tell
    return acc
end concatMap


-- drop :: Int -> [a] -> [a]
on drop(n, xs)
    if n < length of xs then
        items (1 + n) thru -1 of xs
    else
        {}
    end if
end drop


-- 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


-- foldr :: (a -> b -> b) -> b -> [a] -> b
on foldr(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from lng to 1 by -1
            set v to |λ|(item i of xs, v, i, xs)
        end repeat
        return v
    end tell
end foldr


-- 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


-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
    if 0 < n then
        items 1 thru min(n, length of xs) of xs
    else
        {}
    end if
end take


-- Translation of "Improved version of Heap's method (recursive)" found in
-- Robert Sedgewick's PDF document "Permutation Generation Methods"
-- <https://www.cs.princeton.edu/~rs/talks/perms.pdf>

on allPermutations(theList)
    script o
        -- Work list and precalculated indices for its last four items (assuming that many).
        property workList : missing value --(Set to a copy of theList below.)
        property r : (count theList)
        property rMinus1 : r - 1
        property rMinus2 : r - 2
        property rMinus3 : r - 3
        -- Output list and traversal index.
        property output : {}
        property p : 1
        
        -- Recursive handler.
        on prmt(l)
            -- Is the range length covered by this recursion level even?
            set rangeLenEven to ((r - l) mod 2 = 1)
            -- Tail call elimination repeat. Gives way to hard-coding for the lowest three levels.
            repeat with l from l to rMinus3
                -- Recursively permute items (l + 1) thru r of the work list.
                set lPlus1 to l + 1
                prmt(lPlus1)
                -- And again after swaps of item l with each of the items to its right
                -- (if the range l to r is even) or with the rightmost item r - l times
                -- (if the range length is odd). The "recursion" after the last swap will
                -- instead be the next iteration of this tail call elimination repeat.
                if (rangeLenEven) then
                    repeat with swapIdx from r to (lPlus1 + 1) by -1
                        tell my workList's item l
                            set my workList's item l to my workList's item swapIdx
                            set my workList's item swapIdx to it
                        end tell
                        prmt(lPlus1)
                    end repeat
                    set swapIdx to lPlus1
                else
                    repeat (r - lPlus1) times
                        tell my workList's item l
                            set my workList's item l to my workList's item r
                            set my workList's item r to it
                        end tell
                        prmt(lPlus1)
                    end repeat
                    set swapIdx to r
                end if
                tell my workList's item l
                    set my workList's item l to my workList's item swapIdx
                    set my workList's item swapIdx to it
                end tell
                set rangeLenEven to (not rangeLenEven)
            end repeat
            -- Store a copy of the work list's current state.
            set my output's item p to my workList's items
            -- Then five more with the three rightmost items permuted.
            set v1 to my workList's item rMinus2
            set v2 to my workList's item rMinus1
            set v3 to my workList's end
            set my workList's item rMinus1 to v3
            set my workList's item r to v2
            set my output's item (p + 1) to my workList's items
            set my workList's item rMinus2 to v2
            set my workList's item r to v1
            set my output's item (p + 2) to my workList's items
            set my workList's item rMinus1 to v1
            set my workList's item r to v3
            set my output's item (p + 3) to my workList's items
            set my workList's item rMinus2 to v3
            set my workList's item r to v2
            set my output's item (p + 4) to my workList's items
            set my workList's item rMinus1 to v2
            set my workList's item r to v1
            set my output's item (p + 5) to my workList's items
            set p to p + 6
        end prmt
    end script
    
    if (o's r < 3) then
        -- Fewer than three items in the input list.
        copy theList to o's output's beginning
        if (o's r is 2) then set o's output's end to theList's reverse
    else
        -- Otherwise prepare a list to hold (factorial of input list length) permutations …
        copy theList to o's workList
        set factorial to 2
        repeat with i from 3 to o's r
            set factorial to factorial * i
        end repeat
        set o's output to makeList(factorial, missing value)
        -- … and call o's recursive handler.
        o's prmt(1)
    end if
    
    return o's output
end allPermutations

on makeList(limit, filler)
    if (limit < 1) then return {}
    script o
        property lst : {filler}
    end script
    
    set counter to 1
    repeat until (counter + counter > limit)
        set o's lst to o's lst & o's lst
        set counter to counter + counter
    end repeat
    if (counter < limit) then set o's lst to o's lst & o's lst's items 1 thru (limit - counter)
    return o's lst
end makeList

return allPermutations({1, 2, 3, 4})


{{1, 2, 3, 4}, {1, 2, 4, 3}, {1, 3, 4, 2}, {1, 3, 2, 4}, {1, 4, 2, 3}, {1, 4, 3, 2}, {2, 4, 3, 1}, {2, 4, 1, 3}, {2, 3, 1, 4}, {2, 3, 4, 1}, {2, 1, 4, 3}, {2, 1, 3, 4}, {3, 1, 2, 4}, {3, 1, 4, 2}, {3, 2, 4, 1}, {3, 2, 1, 4}, {3, 4, 1, 2}, {3, 4, 2, 1}, {4, 3, 2, 1}, {4, 3, 1, 2}, {4, 2, 1, 3}, {4, 2, 3, 1}, {4, 1, 3, 2}, {4, 1, 2, 3}}


  

You may also check:How to resolve the algorithm Water collected between towers step by step in the 8080 Assembly programming language
You may also check:How to resolve the algorithm Sum and product of an array step by step in the dc programming language
You may also check:How to resolve the algorithm String length step by step in the R programming language
You may also check:How to resolve the algorithm Go Fish step by step in the OCaml programming language
You may also check:How to resolve the algorithm File size step by step in the Clojure programming language