How to resolve the algorithm Determine if a string has all unique characters step by step in the AppleScript programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Determine if a string has all unique characters step by step in the AppleScript programming language

Table of Contents

Problem Statement

Given a character string   (which may be empty, or have a length of zero characters):

Use (at least) these five test values   (strings):

Show all output here on this page.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Determine if a string has all unique characters step by step in the AppleScript programming language

Source code in the applescript programming language

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

on run
    script showSource
        on |λ|(s)
            quoted("'", s) & " (" & length of s & ")"
        end |λ|
    end script
    
    script showDuplicate
        on |λ|(mb)
            script go
                on |λ|(tpl)
                    set {c, ixs} to tpl
                    quoted("'", c) & " at " & intercalate(", ", ixs)
                end |λ|
            end script
            maybe("None", go, mb)
        end |λ|
    end script
    
    fTable("Indices (1-based) of any duplicated characters:\n", ¬
        showSource, showDuplicate, ¬
        duplicatedCharIndices, ¬
        {"", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"})
end run


------------------CHARACTER DUPLICATIONS-------------------

-- duplicatedCharIndices :: String -> Maybe (Char, [Int])
on duplicatedCharIndices(s)
    script positionRecord
        on |λ|(dct, c, i)
            set k to (id of c) as string
            script additional
                on |λ|(xs)
                    insertDict(k, xs & i, dct)
                end |λ|
            end script
            maybe(insertDict(k, {i}, dct), additional, lookupDict(k, dct))
        end |λ|
    end script
    
    script firstDuplication
        on |λ|(sofar, idxs)
            set {iCode, xs} to idxs
            if 1 < length of xs then
                script earliest
                    on |λ|(kxs)
                        if item 1 of xs < (item 1 of (item 2 of kxs)) then
                            Just({chr(iCode), xs})
                        else
                            sofar
                        end if
                    end |λ|
                end script
                maybe(Just({chr(iCode), xs}), earliest, sofar)
            else
                sofar
            end if
        end |λ|
    end script
    
    foldl(firstDuplication, Nothing(), ¬
        assocs(foldl(positionRecord, {name:""}, chars(s))))
end duplicatedCharIndices


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

-- Just :: a -> Maybe a
on Just(x)
    -- Constructor for an inhabited Maybe (option type) value.
    -- Wrapper containing the result of a computation.
    {type:"Maybe", Nothing:false, Just:x}
end Just

-- Nothing :: Maybe a
on Nothing()
    -- Constructor for an empty Maybe (option type) value.
    -- Empty wrapper returned where a computation is not possible.
    {type:"Maybe", Nothing:true}
end Nothing

-- Tuple (,) :: a -> b -> (a, b)
on Tuple(a, b)
    -- Constructor for a pair of values, possibly of two different types.
    {type:"Tuple", |1|:a, |2|:b, length:2}
end Tuple

-- assocs :: Map k a -> [(k, a)]
on assocs(m)
    script go
        on |λ|(k)
            set mb to lookupDict(k, m)
            if true = |Nothing| of mb then
                {}
            else
                {{k, |Just| of mb}}
            end if
        end |λ|
    end script
    concatMap(go, keys(m))
end assocs

-- keys :: Dict -> [String]
on keys(rec)
    (current application's ¬
        NSDictionary's dictionaryWithDictionary:rec)'s allKeys() as list
end keys

-- chr :: Int -> Char
on chr(n)
    character id n
end chr

-- chars :: String -> [Char]
on chars(s)
    characters of s
end chars

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

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

-- 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
        lst
    else
        {}
    end if
end enumFromTo

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

-- fst :: (a, b) -> a
on fst(tpl)
    if class of tpl is record then
        |1| of tpl
    else
        item 1 of tpl
    end if
end fst

-- fTable :: String -> (a -> String) -> (b -> String) -> (a -> b) -> [a] -> String
on fTable(s, xShow, fxShow, f, xs)
    set ys to map(xShow, xs)
    set w to maximum(map(my |length|, ys))
    script arrowed
        on |λ|(a, b)
            justifyRight(w, space, a) & " -> " & b
        end |λ|
    end script
    s & linefeed & unlines(zipWith(arrowed, ¬
        ys, map(compose(fxShow, f), xs)))
end fTable

-- insertDict :: String -> a -> Dict -> Dict
on insertDict(k, v, rec)
    tell current application
        tell dictionaryWithDictionary_(rec) of its NSMutableDictionary
            its setValue:v forKey:(k as string)
            it as record
        end tell
    end tell
end insertDict

-- intercalate :: String -> [String] -> String
on intercalate(delim, xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, delim}
    set str to xs as text
    set my text item delimiters to dlm
    str
end intercalate

-- justifyRight :: Int -> Char -> String -> String
on justifyRight(n, cFiller, strText)
    if n > length of strText then
        text -n thru -1 of ((replicate(n, cFiller) as text) & strText)
    else
        strText
    end if
end justifyRight

-- length :: [a] -> Int
on |length|(xs)
    set c to class of xs
    if list is c or string is c then
        length of xs
    else
        (2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
    end if
end |length|

-- lookupDict :: a -> Dict -> Maybe b
on lookupDict(k, dct)
    -- Just the value of k in the dictionary,
    -- or Nothing if k is not found.
    set ca to current application
    set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s objectForKey:k
    if missing value  v then
        Just(item 1 of ((ca's NSArray's arrayWithObject:v) as list))
    else
        Nothing()
    end if
end lookupDict

-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    -- The list obtained by applying f
    -- to each element of 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

-- maximum :: Ord a => [a] -> a
on maximum(xs)
    script
        on |λ|(a, b)
            if a is missing value or b > a then
                b
            else
                a
            end if
        end |λ|
    end script
    
    foldl(result, missing value, xs)
end maximum

-- maybe :: b -> (a -> b) -> Maybe a -> b
on maybe(v, f, mb)
    -- 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.
    if Nothing of mb then
        v
    else
        tell mReturn(f) to |λ|(Just of mb)
    end if
end maybe

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    -- 2nd class handler function lifted into 1st class script wrapper. 
    if script is class of f then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- quoted :: Char -> String -> String
on quoted(c, s)
    -- string flanked on both sides
    -- by a specified quote character.
    c & s & c
end quoted

-- 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 1 > n then return out
    set dbl to {a}
    
    repeat while (1 < n)
        if 0 < (n mod 2) then set out to out & dbl
        set n to (n div 2)
        set dbl to (dbl & dbl)
    end repeat
    return out & dbl
end replicate

-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
    set c to class of xs
    if list is c then
        if 0 < n then
            items 1 thru min(n, length of xs) of xs
        else
            {}
        end if
    else if string is c then
        if 0 < n then
            text 1 thru min(n, length of xs) of xs
        else
            ""
        end if
    else if script is c then
        set ys to {}
        repeat with i from 1 to n
            set v to |λ|() of xs
            if missing value is v then
                return ys
            else
                set end of ys to v
            end if
        end repeat
        return ys
    else
        missing value
    end if
end take

-- unlines :: [String] -> String
on unlines(xs)
    -- A single string formed by the intercalation
    -- of a list of strings with the newline character.
    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

-- zip :: [a] -> [b] -> [(a, b)]
on zip(xs, ys)
    zipWith(Tuple, xs, ys)
end zip

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
    set lng to min(|length|(xs), |length|(ys))
    if 1 > lng then return {}
    set xs_ to take(lng, xs) -- Allow for non-finite
    set ys_ to take(lng, ys) -- generators like cycle etc
    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


  

You may also check:How to resolve the algorithm Integer sequence step by step in the BASIC programming language
You may also check:How to resolve the algorithm Pi step by step in the Oforth programming language
You may also check:How to resolve the algorithm Towers of Hanoi step by step in the ALGOL W programming language
You may also check:How to resolve the algorithm Ackermann function step by step in the Ada programming language
You may also check:How to resolve the algorithm Generic swap step by step in the MAXScript programming language