From e69e5299ec5fe3d3db8aacbced3a5f70e1d99807 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Sat, 11 Oct 2025 08:42:29 +1100 Subject: [PATCH 1/2] Add `micro-blog` exercise --- config.json | 8 +++ .../practice/micro-blog/.docs/instructions.md | 37 +++++++++++ .../practice/micro-blog/.meta/config.json | 17 +++++ .../practice/micro-blog/.meta/example.fut | 5 ++ .../practice/micro-blog/.meta/tests.toml | 46 +++++++++++++ exercises/practice/micro-blog/micro_blog.fut | 1 + exercises/practice/micro-blog/test.fut | 64 +++++++++++++++++++ generators/exercises/micro_blog.py | 11 ++++ 8 files changed, 189 insertions(+) create mode 100644 exercises/practice/micro-blog/.docs/instructions.md create mode 100644 exercises/practice/micro-blog/.meta/config.json create mode 100644 exercises/practice/micro-blog/.meta/example.fut create mode 100644 exercises/practice/micro-blog/.meta/tests.toml create mode 100644 exercises/practice/micro-blog/micro_blog.fut create mode 100644 exercises/practice/micro-blog/test.fut create mode 100644 generators/exercises/micro_blog.py diff --git a/config.json b/config.json index a3797db..8e2e70c 100644 --- a/config.json +++ b/config.json @@ -329,6 +329,14 @@ "prerequisites": [], "difficulty": 4 }, + { + "slug": "micro-blog", + "name": "Micro Blog", + "uuid": "5ec317e6-1d7e-4253-9aa2-de310588bdaa", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "perfect-numbers", "name": "Perfect Numbers", diff --git a/exercises/practice/micro-blog/.docs/instructions.md b/exercises/practice/micro-blog/.docs/instructions.md new file mode 100644 index 0000000..d6c6cf6 --- /dev/null +++ b/exercises/practice/micro-blog/.docs/instructions.md @@ -0,0 +1,37 @@ +# Instructions + +You have identified a gap in the social media market for very very short posts. +Now that Twitter allows 280 character posts, people wanting quick social media updates aren't being served. +You decide to create your own social media network. + +To make your product noteworthy, you make it extreme and only allow posts of 5 or less characters. +Any posts of more than 5 characters should be truncated to 5. + +To allow your users to express themselves fully, you allow Emoji and other Unicode. + +The task is to truncate input strings to 5 characters. + +## Text Encodings + +Text stored digitally has to be converted to a series of bytes. +There are 3 ways to map characters to bytes in common use. + +- **ASCII** can encode English language characters. + All characters are precisely 1 byte long. +- **UTF-8** is a Unicode text encoding. + Characters take between 1 and 4 bytes. +- **UTF-16** is a Unicode text encoding. + Characters are either 2 or 4 bytes long. + +UTF-8 and UTF-16 are both Unicode encodings which means they're capable of representing a massive range of characters including: + +- Text in most of the world's languages and scripts +- Historic text +- Emoji + +UTF-8 and UTF-16 are both variable length encodings, which means that different characters take up different amounts of space. + +Consider the letter 'a' and the emoji 'πŸ˜›'. +In UTF-16 the letter takes 2 bytes but the emoji takes 4 bytes. + +The trick to this exercise is to use APIs designed around Unicode characters (codepoints) instead of Unicode codeunits. diff --git a/exercises/practice/micro-blog/.meta/config.json b/exercises/practice/micro-blog/.meta/config.json new file mode 100644 index 0000000..cda53af --- /dev/null +++ b/exercises/practice/micro-blog/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "micro_blog.fut" + ], + "test": [ + "test.fut" + ], + "example": [ + ".meta/example.fut" + ] + }, + "blurb": "Given an input string, truncate it to 5 characters." +} diff --git a/exercises/practice/micro-blog/.meta/example.fut b/exercises/practice/micro-blog/.meta/example.fut new file mode 100644 index 0000000..4d88640 --- /dev/null +++ b/exercises/practice/micro-blog/.meta/example.fut @@ -0,0 +1,5 @@ +def truncate (phrase: []u8): []u8 = + let (index, count) = loop (index, count) = (0, 0) while index < (length phrase) && count < 6 do + if (phrase[index] & 0xc0) == 0x80 then (index + 1, count) else (index + 1, count + 1) + in + if count == 6 then phrase[0:index - 1] else phrase diff --git a/exercises/practice/micro-blog/.meta/tests.toml b/exercises/practice/micro-blog/.meta/tests.toml new file mode 100644 index 0000000..f23ff0b --- /dev/null +++ b/exercises/practice/micro-blog/.meta/tests.toml @@ -0,0 +1,46 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[b927b57f-7c98-42fd-8f33-fae091dc1efc] +description = "English language short" + +[a3fcdc5b-0ed4-4f49-80f5-b1a293eac2a0] +description = "English language long" + +[01910864-8e15-4007-9c7c-ac956c686e60] +description = "German language short (broth)" + +[f263e488-aefb-478f-a671-b6ba99722543] +description = "German language long (bear carpet β†’ beards)" + +[0916e8f1-41d7-4402-a110-b08aa000342c] +description = "Bulgarian language short (good)" + +[bed6b89c-03df-4154-98e6-a61a74f61b7d] +description = "Greek language short (health)" + +[485a6a70-2edb-424d-b999-5529dbc8e002] +description = "Maths short" + +[8b4b7b51-8f48-4fbe-964e-6e4e6438be28] +description = "Maths long" + +[71f4a192-0566-4402-a512-fe12878be523] +description = "English and emoji short" + +[6f0f71f3-9806-4759-a844-fa182f7bc203] +description = "Emoji short" + +[ce71fb92-5214-46d0-a7f8-d5ba56b4cc6e] +description = "Emoji long" + +[5dee98d2-d56e-468a-a1f2-121c3f7c5a0b] +description = "Royal Flush?" diff --git a/exercises/practice/micro-blog/micro_blog.fut b/exercises/practice/micro-blog/micro_blog.fut new file mode 100644 index 0000000..f34c87e --- /dev/null +++ b/exercises/practice/micro-blog/micro_blog.fut @@ -0,0 +1 @@ +def truncate (phrase: []u8): []u8 = ??? diff --git a/exercises/practice/micro-blog/test.fut b/exercises/practice/micro-blog/test.fut new file mode 100644 index 0000000..976a58f --- /dev/null +++ b/exercises/practice/micro-blog/test.fut @@ -0,0 +1,64 @@ +import "micro_blog" + +-- English language short +-- == +-- input { "Hi" } +-- output { "Hi" } + +-- English language long +-- == +-- input { "Hello there" } +-- output { "Hello" } + +-- German language short (broth) +-- == +-- input { "brΓΌhe" } +-- output { "brΓΌhe" } + +-- German language long (bear carpet β†’ beards) +-- == +-- input { "BΓ€rteppich" } +-- output { "BΓ€rte" } + +-- Bulgarian language short (good) +-- == +-- input { "Π”ΠΎΠ±ΡŠΡ€" } +-- output { "Π”ΠΎΠ±ΡŠΡ€" } + +-- Greek language short (health) +-- == +-- input { "υγΡιά" } +-- output { "υγΡιά" } + +-- Maths short +-- == +-- input { "a=Ο€rΒ²" } +-- output { "a=Ο€rΒ²" } + +-- Maths long +-- == +-- input { "βˆ…βŠŠβ„•βŠŠβ„€βŠŠβ„šβŠŠβ„βŠŠβ„‚" } +-- output { "βˆ…βŠŠβ„•βŠŠβ„€" } + +-- English and emoji short +-- == +-- input { "Fly πŸ›«" } +-- output { "Fly πŸ›«" } + +-- Emoji short +-- == +-- input { "πŸ’‡" } +-- output { "πŸ’‡" } + +-- Emoji long +-- == +-- input { "β„πŸŒ‘πŸ€§πŸ€’πŸ₯πŸ•°πŸ˜€" } +-- output { "β„πŸŒ‘πŸ€§πŸ€’πŸ₯" } + +-- Royal Flush? +-- == +-- input { "πŸƒŽπŸ‚ΈπŸƒ…πŸƒ‹πŸƒπŸƒπŸƒŠ" } +-- output { "πŸƒŽπŸ‚ΈπŸƒ…πŸƒ‹πŸƒ" } + +let main (phrase: []u8): []u8 = + truncate phrase diff --git a/generators/exercises/micro_blog.py b/generators/exercises/micro_blog.py new file mode 100644 index 0000000..dd2f608 --- /dev/null +++ b/generators/exercises/micro_blog.py @@ -0,0 +1,11 @@ +def gen_test_case(prop, description, inp, expected, f): + phrase = inp["phrase"] + f.write(f"-- {description}\n") + f.write("-- ==\n") + f.write(f'-- input {{ "{phrase}" }}\n') + f.write(f'-- output {{ "{expected}" }}\n\n') + + +def gen_main(f): + f.write("let main (phrase: []u8): []u8 =\n") + f.write(" truncate phrase\n") From 3d46728f36871bbc36ae3ef3ca6ad03d6e3ea2e7 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Sat, 11 Oct 2025 09:04:24 +1100 Subject: [PATCH 2/2] uniqueness types used in micro-blog --- exercises/practice/micro-blog/.meta/example.fut | 2 +- exercises/practice/micro-blog/micro_blog.fut | 2 +- exercises/practice/micro-blog/test.fut | 2 +- generators/exercises/micro_blog.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/practice/micro-blog/.meta/example.fut b/exercises/practice/micro-blog/.meta/example.fut index 4d88640..02cd01c 100644 --- a/exercises/practice/micro-blog/.meta/example.fut +++ b/exercises/practice/micro-blog/.meta/example.fut @@ -1,4 +1,4 @@ -def truncate (phrase: []u8): []u8 = +def truncate (phrase: *[]u8): *[]u8 = let (index, count) = loop (index, count) = (0, 0) while index < (length phrase) && count < 6 do if (phrase[index] & 0xc0) == 0x80 then (index + 1, count) else (index + 1, count + 1) in diff --git a/exercises/practice/micro-blog/micro_blog.fut b/exercises/practice/micro-blog/micro_blog.fut index f34c87e..534fc9b 100644 --- a/exercises/practice/micro-blog/micro_blog.fut +++ b/exercises/practice/micro-blog/micro_blog.fut @@ -1 +1 @@ -def truncate (phrase: []u8): []u8 = ??? +def truncate (phrase: *[]u8): *[]u8 = ??? diff --git a/exercises/practice/micro-blog/test.fut b/exercises/practice/micro-blog/test.fut index 976a58f..39cfb3f 100644 --- a/exercises/practice/micro-blog/test.fut +++ b/exercises/practice/micro-blog/test.fut @@ -60,5 +60,5 @@ import "micro_blog" -- input { "πŸƒŽπŸ‚ΈπŸƒ…πŸƒ‹πŸƒπŸƒπŸƒŠ" } -- output { "πŸƒŽπŸ‚ΈπŸƒ…πŸƒ‹πŸƒ" } -let main (phrase: []u8): []u8 = +let main (phrase: *[]u8): *[]u8 = truncate phrase diff --git a/generators/exercises/micro_blog.py b/generators/exercises/micro_blog.py index dd2f608..1df9cfc 100644 --- a/generators/exercises/micro_blog.py +++ b/generators/exercises/micro_blog.py @@ -7,5 +7,5 @@ def gen_test_case(prop, description, inp, expected, f): def gen_main(f): - f.write("let main (phrase: []u8): []u8 =\n") + f.write("let main (phrase: *[]u8): *[]u8 =\n") f.write(" truncate phrase\n")