diff --git a/config.json b/config.json index cb944adc..e5474a15 100644 --- a/config.json +++ b/config.json @@ -364,7 +364,7 @@ "uuid": "59c6319b-2799-4e4c-bef1-e28626878446", "practices": [], "prerequisites": [], - "difficulty": 2 + "difficulty": 4 }, { "slug": "roman-numerals", diff --git a/exercises/practice/reverse-string/.meta/config.json b/exercises/practice/reverse-string/.meta/config.json index 34a81d6c..94ff37fe 100644 --- a/exercises/practice/reverse-string/.meta/config.json +++ b/exercises/practice/reverse-string/.meta/config.json @@ -2,6 +2,9 @@ "authors": [ "fapdash" ], + "contributors": [ + "kmarker1101" + ], "files": { "solution": [ "reverse-string.el" diff --git a/exercises/practice/reverse-string/.meta/example.el b/exercises/practice/reverse-string/.meta/example.el index 73fab7ac..16465ba6 100644 --- a/exercises/practice/reverse-string/.meta/example.el +++ b/exercises/practice/reverse-string/.meta/example.el @@ -5,9 +5,33 @@ ;;; Code: -(defun reverse-string (value) - (reverse value)) +(require 'cl-lib) +(require 'ucs-normalize) + +(defun combining-mark-p (char) + (let ((category (get-char-code-property char 'general-category))) + (or (string= category "Mn") ; Nonspacing_Mark + (string= category "Mc") ; Spacing_Mark + (string= category "Me")))) ; Enclosing_Mark +(defun split-grapheme-clusters (str) + (let ((clusters '()) + (i 0) + (len (length str))) + (while (< i len) + (let ((start i) + (char (aref str i))) + (setq i (1+ i)) + (while (and (< i len) + (combining-mark-p (aref str i))) + (setq i (1+ i))) + (push (substring str start i) clusters))) + (nreverse clusters))) + +(defun reverse-string (value) + (let* ((decomposed (ucs-normalize-NFD-string value)) + (clusters (split-grapheme-clusters decomposed))) + (apply #'concat (reverse clusters)))) (provide 'reverse-string) ;;; reverse-string.el ends here diff --git a/exercises/practice/reverse-string/.meta/tests.toml b/exercises/practice/reverse-string/.meta/tests.toml index 0b04c4cd..0c313cc5 100644 --- a/exercises/practice/reverse-string/.meta/tests.toml +++ b/exercises/practice/reverse-string/.meta/tests.toml @@ -26,3 +26,12 @@ description = "a palindrome" [b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c] description = "an even-sized word" + +[1bed0f8a-13b0-4bd3-9d59-3d0593326fa2] +description = "wide characters" + +[93d7e1b8-f60f-4f3c-9559-4056e10d2ead] +description = "grapheme cluster with pre-combined form" + +[1028b2c1-6763-4459-8540-2da47ca512d9] +description = "grapheme clusters" diff --git a/exercises/practice/reverse-string/reverse-string-test.el b/exercises/practice/reverse-string/reverse-string-test.el index dc0535e4..41e4093c 100644 --- a/exercises/practice/reverse-string/reverse-string-test.el +++ b/exercises/practice/reverse-string/reverse-string-test.el @@ -32,6 +32,15 @@ (ert-deftest an-even-sized-word () (should (string= (reverse-string "drawer") "reward"))) +(ert-deftest wide-characters () + (should (string= (reverse-string "子猫") "猫子"))) + +(ert-deftest grapheme-cluster-with-pre-combined-form () + (should (string= (reverse-string "Würstchenstand") "dnatsnehctsrüW"))) + +(ert-deftest grapheme-cluster () + (should (string= (reverse-string "ผู้เขียนโปรแกรม") "มรกแรปโนยขีเผู้"))) + (provide 'reverse-string-test) ;;; reverse-string-test.el ends here