-
Notifications
You must be signed in to change notification settings - Fork 4
2. English ‐ CoxyHasList
Company : Coxygen Global Date : 10-09-2025 Licence : MIT
- 🔧 Full Module (copy-paste ready)
- ✅ Mini Test Helper:
assert - 📏 List Size:
sizeOfList - 🔎 Find/Check Items:
isItemInTheList,findPosItemList,findElemList - 🪪 Safe Access:
firstElemList - 🧰 Keep/Remove with Conditions:
filterItemsList,removeItemList - ➕ Combining Lists:
joinList - ✂️ Drop Ends:
notHeadList,notLastList,bodyElemList - 🧱 Add to Front:
addItemList - 🔁 Reverse:
reverseList - 🎯 Last Element (safe):
lastElemList - 🗂️ Sort Asc/Des:
orderList - 🔠 Case Change (no imports):
strToUpper,strToLower - 🧼 String Cleanups:
removeSpaces,removeVowels,removeNoAlphabetLetters,removeSymbols,lettersOnly - ✍️ Find & Replace (substring):
findAndReplaceStrings,startsWith - 🏆 Extremes:
biggestItemList,smallestItemList - 📚 Glossary
module CoxyHasList where
-- 2) ✅ Mini Test Helper
assert :: (Eq a, Show a) => String -> a -> a -> String
assert itemNameTested expected actual =
if expected == actual
then "[Pass] : " ++ itemNameTested ++ " Answer : " ++ show actual
else "[Fail] : " ++ itemNameTested ++
" Expected : " ++ show expected ++
" But got : " ++ show actual
-- 3) 📏 List Size
sizeOfList :: [a] -> Int
sizeOfList [] = 0
sizeOfList [_] = 1
sizeOfList (_:xs) = 1 + sizeOfList xs
-- 4) 🔎 Find/Check Items
isItemInTheList :: (Eq a) => a -> [a] -> Bool
isItemInTheList _ [] = False
isItemInTheList item (x:xs)
| item == x = True
| otherwise = isItemInTheList item xs
-- Alias used by main in your text
findElemList :: Eq a => a -> [a] -> Bool
findElemList = isItemInTheList
findPosItemList :: Eq a => [a] -> a -> Maybe Int
findPosItemList xs y = go 0 xs
where
go :: Eq a => Int -> [a] -> Maybe Int
go _ [] = Nothing
go i (x:xs)
| x == y = Just i
| otherwise = go (i + 1) xs
-- 5) 🪪 Safe Access
firstElemList :: [a] -> Maybe a
firstElemList [] = Nothing
firstElemList (x :_) = Just x
-- 6) 🧰 Keep/Remove with Conditions
filterItemsList :: (a -> Bool) -> [a] -> [a]
filterItemsList _ [] = []
filterItemsList p (x:xs)
| p x = x : filterItemsList p xs
| otherwise = filterItemsList p xs
removeItemList :: Eq a => a -> [a] -> [a]
removeItemList x = filter (\y -> y /= x)
-- 7) ➕ Combining Lists
joinList :: [a] -> [a] -> [a]
joinList xs ys = xs ++ ys
-- 8) ✂️ Drop Ends
notHeadList :: [a] -> [a]
notHeadList [] = []
notHeadList (_:xs) = xs
notLastList :: [a] -> [a]
notLastList [] = []
notLastList [_] = []
notLastList (x:xs) = x : notLastList xs
-- Two equivalent versions were present; keep the simpler one:
bodyElemList :: [a] -> [a]
bodyElemList xs = notLastList (notHeadList xs)
-- 9) 🧱 Add to Front
addItemList :: a -> [a] -> [a]
addItemList a as = a : as
-- 10) 🔁 Reverse
reverseList :: [a] -> [a]
reverseList [] = []
reverseList (a:as) = reverseList as ++ [a]
-- 11) 🎯 Last Element (safe)
lastElemList :: [a] -> Maybe a
lastElemList [] = Nothing
lastElemList [a] = Just a
lastElemList (_:as) = lastElemList as
-- 12) 🗂️ Sort Asc/Des (quicksort style)
orderList :: (Ord a) => [a] -> String -> [a]
orderList [] _ = []
orderList (x:xs) dir
| dir == "asc" =
let smaller = [y | y <- xs, y <= x]
larger = [y | y <- xs, y > x]
in orderList smaller dir ++ [x] ++ orderList larger dir
| dir == "des" =
let smaller = [y | y <- xs, y > x]
larger = [y | y <- xs, y <= x]
in orderList smaller dir ++ [x] ++ orderList larger dir
| otherwise = []
-- 13) 🔠 Case Change (ASCII only, no imports)
strToUpper :: String -> String
strToUpper str =
[ if 'a' <= c && c <= 'z'
then toEnum (fromEnum c - 32)
else c
| c <- str ]
strToLower :: String -> String
strToLower str =
[ if 'A' <= c && c <= 'Z'
then toEnum (fromEnum c + 32)
else c
| c <- str ]
-- 14) 🧼 String Cleanups
removeSpaces :: String -> String
removeSpaces str = [ y | y <- str
, y /= '\n' && y /= '\r' && y /= ' ' && y /= '\t' ]
removeVowels :: String -> String
removeVowels str = [ y | y <- str, y `notElem` "aeiou" ]
-- fixed: keep only alphabet letters (ASCII)
removeNoAlphabetLetters :: String -> String
removeNoAlphabetLetters str = [ y | y <- str
, y `elem` (['a'..'z'] ++ ['A'..'Z']) ]
-- this actually "keeps only letters" (name suggests removal)
removeSymbols :: String -> String
removeSymbols str = [ y | y <- str
, y `elem` (['a'..'z'] ++ ['A'..'Z']) ]
-- keep only consonant letters (ASCII)
lettersOnly :: String -> String
lettersOnly str =
[ y | y <- str
, y `elem` (['a'..'z'] ++ ['A'..'Z'])
, y `notElem` "aeiou" ]
-- 15) ✍️ Find & Replace (substring, non-overlapping)
findAndReplaceStrings :: String -> String -> String -> String
findAndReplaceStrings old new s
| null old = s
| otherwise = go s
where
n = length old
go [] = []
go str
| startsWith old str = new ++ go (drop n str)
| otherwise = head str : go (tail str)
startsWith :: String -> String -> Bool
startsWith [] _ = True
startsWith _ [] = False
startsWith (x:xs) (y:ys) = x == y && startsWith xs ys
-- 16) 🏆 Extremes
biggestItemList :: Ord a => [a] -> a
biggestItemList [] = error "biggestItemList: empty list"
biggestItemList [x] = x
biggestItemList (x:xs) =
let m = biggestItemList xs
in if x >= m then x else m
smallestItemList :: Ord a => [a] -> a
smallestItemList [] = error "smallestItemList: empty list"
smallestItemList [x] = x
smallestItemList (x:xs) =
let m = smallestItemList xs
in if x <= m then x else m-
🧠 What: Tiny helper to check expected vs actual.
-
🧪 Use:
assert "reverse" [3,2,1] (reverseList [1,2,3])
main ::IO()
main = do
print $ findElemList 3 [2,5,6]
let l1 = [2,3,4]
let l2 = [4,6,7,8,9,3,5,1,56,1]
print $ joinList l1 l2
print $ assert "Test 3 functions head, last and body using [4,6,7,8,9]" [6,7,8] $ bodyElemList l2
print $ addItemList 78 l1
print $ reverseList l2
print $ assert "List reversed " [9,8,7,6,4] $ reverseList [4,6,7,8,9]
print $ lastElemList l2
print $ orderList l2 "des"
print $ strToUpper "bernarD"
print $ strToLower "THIS IS BIGG"
print $ removeSpaces "this is nothing \r \n "
print $ removeVowels "hellow world"
print $ removeSymbols "Why? is th94375478484 .<>"
let string = "the quick brown fox jumps over the lazy dog"
print $ lettersOnly string
-- print $ findAndReplace "the" "256" string
print $ findAndReplaceStrings "the" "145" string
print $ findAndReplaceStrings "a" "@" string
- Counts items recursively.
Why patterns?
-
[]has size 0. -
[_](one element) has size 1. - Otherwise, strip head and add 1.
-
isItemInTheList→ boolean membership. -
findPosItemList→ first index (Maybe Int). -
findElemList→ alias used by your originalmain.
- Safe head (returns
Nothingon empty).
-
filterItemsList p xskeeps items wherep xisTrue. -
removeItemList v xsremoves allv.
- Concatenates two lists.
- Remove first, last, or both ends.
- Prepend an element: O(1).
- Classic recursive reverse.
- Safe last using
Maybe.
- Quicksort style;
"asc"or"des". - Any other flag →
[](by your design).
- ASCII-only transformations using
fromEnum/toEnum.
-
Fixes made:
-
removeNoAlphabetLetters: was using"a..Z"(not a range). Now uses ranges properly. -
removeSymbols: name suggests “remove symbols”, and the implementation keeps only letters (so symbols are removed). Behavior preserved, just calling it out.
-
- Non-overlapping replacement (standard behavior).
- Works for chars, words, digit groups—anything that’s a substring.
- Recursive extrema (throw error on empty list—same behavior you had).
- Eq / Ord: Typeclasses for equality / ordering.
-
Maybe: Optional type:
Just xorNothing. -
Pattern Matching: Matching shapes like
x:xs,[]. -
Guards:
| condition = ...choosing branches by boolean tests. -
List Comprehension:
[y | y <- xs, predicate y]. - Recursion: Function calling itself on smaller input.
-
Concatenation (
++): Join lists end-to-end.
Awesome! Here’s a Practice Pack for CoxyHasList—with numbered exercises, tiny hints, and a solutions section you can reveal when ready. I kept everything compatible with your module (no imports). 🧩🚀
- Warm-ups
- List Core
- Safe Access & Search
- List Transformations
- Sorting
- String Utilities
- Find & Replace
- Extremes
- Bonus Challenges
- Solutions
1.1 — Count elements
Write countElems :: [a] -> Int using only sizeOfList.
Hint: It’s basically a wrapper.
1.2 — Is empty?
Write isEmpty :: [a] -> Bool using pattern matching (no library calls).
Hint: [] vs (_:_).
2.1 — Keep evens
keepEvens :: [Int] -> [Int] using filterItemsList.
Hint: Use \n -> n mod 2 == 0.
2.2 — Remove zeros
dropZeros :: [Int] -> [Int] using removeItemList.
2.3 — Concat three
join3 :: [a] -> [a] -> [a] -> [a] using joinList only.
3.1 — First or default
firstOr :: a -> [a] -> a
If list is empty, return the default; else the first. Use firstElemList.
3.2 — Find index or -1
findIndexOrNeg1 :: Eq a => a -> [a] -> Int using findPosItemList.
3.3 — Contains all?
containsAll :: Eq a => [a] -> [a] -> Bool
Return True if every element of list b is found in list a. Use isItemInTheList.
4.1 — Drop both ends safely
middleOrEmpty :: [a] -> [a] using bodyElemList.
4.2 — Push front
pushFrontMany :: [a] -> [a] -> [a] that prepends all items of the first list to the second using addItemList (call it repeatedly).
4.3 — Reverse twice
rev2 :: [a] -> [a] using reverseList only (twice).
5.1 — Sort asc
sortAsc :: Ord a => [a] -> [a] using orderList.
5.2 — Sort desc
sortDesc :: Ord a => [a] -> [a] using orderList.
5.3 — Top-3 largest
top3 :: Ord a => [a] -> [a]
Sort descending with orderList and keep first 3 (if available).
6.1 — Title the first letter
capitalizeFirst :: String -> String
Uppercase the first character only using strToUpper + pattern matching.
6.2 — All caps, no spaces
shout :: String -> String using strToUpper then removeSpaces.
6.3 — Only letters, lowercase
lettersLower :: String -> String using removeSymbols then strToLower.
7.1 — Censor a word
censor :: String -> String -> String
Replace all occurrences of a word with "***" using findAndReplaceStrings.
7.2 — Replace digits with X
maskDigits :: String -> String
Replace "0".."9" by "X" by repeatedly calling findAndReplaceStrings.
8.1 — Safe max
safeMax :: Ord a => [a] -> Maybe a
Use biggestItemList for non-empty; otherwise Nothing.
8.2 — Safe min
safeMin :: Ord a => [a] -> Maybe a
Use smallestItemList for non-empty; otherwise Nothing.
9.1 — Unique (preserve firsts)
unique :: Eq a => [a] -> [a]
Keep first occurrences only. Use isItemInTheList + recursion.
9.2 — Anagram key
anagramKey :: String -> String
Sort letters ascending using orderList (works on Char).
9.3 — Word count (simple)
wordCount :: String -> Int
Assume words are separated by single spaces (no trim). Use recursion or split by scanning.
Tip: paste these below your module. They only use functions you already have.
-- 1.1
countElems :: [a] -> Int
countElems = sizeOfList
-- 1.2
isEmpty :: [a] -> Bool
isEmpty [] = True
isEmpty _ = False
-- 2.1
keepEvens :: [Int] -> [Int]
keepEvens xs = filterItemsList (\n -> n `mod` 2 == 0) xs
-- 2.2
dropZeros :: [Int] -> [Int]
dropZeros = removeItemList 0
-- 2.3
join3 :: [a] -> [a] -> [a] -> [a]
join3 a b c = joinList (joinList a b) c
-- 3.1
firstOr :: a -> [a] -> a
firstOr def xs =
case firstElemList xs of
Nothing -> def
Just v -> v
-- 3.2
findIndexOrNeg1 :: Eq a => a -> [a] -> Int
findIndexOrNeg1 x xs =
case findPosItemList xs x of
Nothing -> (-1)
Just i -> i
-- 3.3
containsAll :: Eq a => [a] -> [a] -> Bool
containsAll as [] = True
containsAll as (b:bs) = isItemInTheList b as && containsAll as bs
-- 4.1
middleOrEmpty :: [a] -> [a]
middleOrEmpty = bodyElemList
-- 4.2
pushFrontMany :: [a] -> [a] -> [a]
pushFrontMany [] acc = acc
pushFrontMany (x:xs) acc = pushFrontMany xs (addItemList x acc)
-- 4.3
rev2 :: [a] -> [a]
rev2 = reverseList . reverseList
-- 5.1
sortAsc :: Ord a => [a] -> [a]
sortAsc xs = orderList xs "asc"
-- 5.2
sortDesc :: Ord a => [a] -> [a]
sortDesc xs = orderList xs "des"
-- 5.3
top3 :: Ord a => [a] -> [a]
top3 xs =
let sorted = sortDesc xs
in case sorted of
(a:b:c:_) -> [a,b,c]
_ -> sorted -- if fewer than 3, return what we have
-- 6.1
capitalizeFirst :: String -> String
capitalizeFirst [] = []
capitalizeFirst (c:cs) = head (strToUpper [c]) : cs
-- explanation: strToUpper works on whole strings, so call it on [c]
-- 6.2
shout :: String -> String
shout = removeSpaces . strToUpper
-- 6.3
lettersLower :: String -> String
lettersLower = strToLower . removeSymbols
-- 7.1
censor :: String -> String -> String
censor word s = findAndReplaceStrings word "***" s
-- 7.2
maskDigits :: String -> String
maskDigits s =
let step d acc = findAndReplaceStrings [d] "X" acc
in step '0' . step '1' . step '2' . step '3' . step '4'
. step '5' . step '6' . step '7' . step '8' . step '9' $ s
-- 8.1
safeMax :: Ord a => [a] -> Maybe a
safeMax [] = Nothing
safeMax xs = Just (biggestItemList xs)
-- 8.2
safeMin :: Ord a => [a] -> Maybe a
safeMin [] = Nothing
safeMin xs = Just (smallestItemList xs)
-- 9.1
unique :: Eq a => [a] -> [a]
unique [] = []
unique (x:xs) =
let rest = unique xs
in if isItemInTheList x rest
then rest
else addItemList x rest
-- 9.2
anagramKey :: String -> String
anagramKey s = orderList s "asc"
-- 9.3 (simple; expects single-space-separated words)
wordCount :: String -> Int
wordCount [] = 0
wordCount (' ':xs) = 1 + wordCount (dropWhileSpace xs)
wordCount (_:xs) = wordCount xs
where
dropWhileSpace [] = []
dropWhileSpace (' ':rest) = dropWhileSpace rest
dropWhileSpace r = rYou can use your assert to sanity-check:
-- countElems / isEmpty
assert "countElems" 3 (countElems [1,2,3])
assert "isEmpty True" True (isEmpty [])
assert "isEmpty False" False (isEmpty [1])
-- keepEvens / dropZeros
assert "keepEvens" [2,4,6] (keepEvens [1,2,3,4,5,6])
assert "dropZeros" [1,2,3] (dropZeros [0,1,0,2,0,3])
-- join3
assert "join3" [1,2,3,4,5] (join3 [1] [2,3] [4,5])
-- firstOr / findIndexOrNeg1
assert "firstOr" 10 (firstOr 10 [])
assert "findIndexOrNeg1" 2 (findIndexOrNeg1 'c' "abcde")
-- pushFrontMany / rev2
assert "pushFrontMany" [3,2,1,4,5] (pushFrontMany [1,2,3] [4,5])
assert "rev2" [1,2,3] (rev2 [1,2,3])
-- sortAsc / sortDesc / top3
assert "sortAsc" [1,2,3] (sortAsc [3,1,2])
assert "sortDesc" [3,2,1] (sortDesc [3,1,2])
assert "top3" [9,8,7] (top3 [1,9,3,7,8,2])
-- strings
assert "capitalizeFirst" "Hello" (capitalizeFirst "hello")
assert "shout" "HELLOWORLD!" (shout "Hello World!")
assert "lettersLower" "abcxyz" (lettersLower "Abc-XYZ!")
-- replace / mask
assert "censor" "the *** and ***" (censor "cat" "the cat and cat")
assert "maskDigits" "abcXXdef" (maskDigits "abc12def")
-- extremes
assert "safeMax" (Just 9) (safeMax [1,9,2])
assert "safeMin" (Just 1) (safeMin [1,9,2])
-- unique / anagramKey / wordCount
assert "unique" [3,2,1] (unique [1,2,3,2,1] |> sortDesc)
where (|>) = flip ($)
assert "anagramKey" "aelpp" (anagramKey "apple")
assert "wordCount" 3 (wordCount "one two three")-- simple crud demo with Cart
module Main where
type Id = String
type Name = String
type Mark = Int
data Item = Item (Id, Name, Mark) deriving (Show)
data Cart = Cart
{ unCart :: [Item]
} deriving (Show)
createItem :: Id -> Name -> Mark -> Maybe Item
createItem a b c
| a == "" || b == "" || c < 0 || c > 100 = Nothing
| otherwise = Just (Item (a, b, c))
emptyCart :: Cart
emptyCart = Cart []
createCart :: Maybe Item -> Cart -> Cart
createCart Nothing c = c
createCart (Just a) (Cart c) = Cart (a : c)
updateCart :: Id -> Maybe Item -> Cart -> Cart
updateCart _ Nothing cart = cart
updateCart _ _ (Cart []) = Cart []
updateCart cartId (Just newItemToReplace) (Cart items) =
Cart [if i == cartId then newItemToReplace else y | y@(Item (i,_,_)) <- items]
filterCart :: Id -> Cart -> Cart
filterCart _ (Cart []) = Cart []
filterCart targetId (Cart items) =
Cart([y|y@(Item(i,_,_)) <- items, i /= targetId])
displayItem :: Id -> Cart -> String
displayItem _ (Cart []) = "Cart is empty"
displayItem i_ (Cart (Item(i,n,m):xs)) =
if i_ == i
then
"Item id : "++ i ++", name : "++n++", mark : "++ show m
else
displayItem i_ (Cart xs)
main :: IO ()
main = do
let s1 = createItem "001" "james" 25
let s2 = createItem "002" "mary" 50
let s3 = createItem "003" "ruth" 22
let s4 = createItem "004" "john" 29
let s5 = createItem "003" "gilbert" 55
let c1 = createCart s1 emptyCart
let c2 = createCart s2 c1
let c3 = createCart s3 c2
let c4 = createCart s4 c3
print c4
let c4Update = updateCart "003" s5 c4
print c4Update
-- Example: filter out all with Id "002"
let c4Filtered = filterCart "002" c4Update
print c3
print $ displayItem "002" c3
Bernard Sibanda is a global Technology Entrepreneur, Web3 and Software Consultant with a deep focus on Cardano Blockchain, Midnight and Community building.
Key Positions:
- Founder, CTO, Developer Advocate cohort #1, Fullstake Developer, Cardano Ambassador, Catalyst Project Manager, DREP-WIMS:
- Co-founder of ABL Tech and Cardano Africa Live
- EBU-certified Plutus Pioneer (Plutus/Haskell)
- Cohort #1 Plutus Pioneer Developer
- Catalyst Community Reviewer & Funded Projects Manager
-
DRep for WIMS-Cardano (ID:
drep1yguj8zu48n99pv70yl6ckzt9hdgjy8yjnlqs2uyzcpafnjgu4vkul) - Intersect Developer Advocate
- Intersect Committe Member 2025-2026
- Cardano Marketer,Promoter and blogger
- Cardano Open Source Contributor
- Cardano communities and events organizer and builder
- Cardano Ambassador for South Africa
Official links:
- Stablecoins Dex
- Coxygen Global Universities
- WIMS Cardano Global
- Cardano Africa Live
- WIMS Cardano Videos
- Cardano Smart Contract Videos
- Fullstack IT Consulting
Social links: