Бесплатный курс по Racket. Зарегистрируйтесь для отслеживания прогресса →

Racket: Сравнение строк и символов, предикаты

Сравнение строк и строковых символов

Пусть заголовок вас не смущает: сравнить char и string не получится, ведь это разные типы!

Но и пару строк или пару строковых символов сравнить с помощью функций "=" и "<" не получится: в Racket эти функции работают только с числами. Чтобы соотносить между собой значения string и char в коде на Racket, вам нужно использовать функции с именами вида string=? и char<? или, проще говоря, "тип"+"оператор"+"?". И запомнить, и использовать эти функции довольно просто:

(char<?   #\a #\b)     ; #t
(char>?   #\c #\b #\a) ; #t
(char=?   #\a #\b)     ; #f
(string<? "a" "b")     ; #t
(string>? "c" "b" "a") ; #t
(string=? "a" "b")     ; #f

Глядя на этот пример, вы и сами сможете догадаться, как работают функции string<=? и char>=? :)

Регистронезависимость

Упомянутые функции сравнивают строки и строковые символы буквально или, как говорят, лексикографически. Однако при работе с текстом часто нужно сопоставлять строки без учёта регистра, или производить регистронезависимое сравнение (case-insensitive comparison). В Racket для каждой функции сравнения есть регистронезависимый вариант, имеющий суффикс -ci ("case-insensitive"):

(string=?    "Apple" "apple") ; #f
(string-ci=? "Apple" "apple") ; #t
(char>?      #\C     #\b)     ; #f
(char-ci>?   #\C     #\b)     ; #t

Группы символов

Строковые символы могут быть буквами, цифрами, знаками препинания. Более того, буквы могут принадлежать разным алфавитам, одни из которых имеют разные регистры, к другим же данная концепция в принципе не применима. Получается, что некоторый строковый символ может принадлежать к одной или сразу к нескольким группам символов. Проверить принадлежность к основным группам символов можно с помощью набора предикатов с именами вида char-alphabetic? и char-lower-case?:

(char-alphabetic? #\a)    ; #t
(char-alphabetic? #\u3BB) ; #t — "λ" это буква, пусть и греческая
(char-lower-case? #\a)    ; #t
(char-lower-case? #\A)    ; #f

Предикатов такого вида в Racket больше десятка, почитать обо всех вы сможете в документации.

Отдельных функций, которые бы проверяли, что все символы некоторой строки принадлежат к некоторой группе, Racket не предоставляет. Впрочем, организовать такую проверку довольно просто, если взять функцию andmap, работающую со списками, и объединить со string->list:

(andmap char-alphabetic? (string->list "asd"))  ; #t
(andmap char-alphabetic? (string->list "r2d2")) ; #f

Если заменить andmap на ormap, то вместо проверки на "все символы…" получится проверка на "хотя бы один символ…"!

Задание

Реализуйте два предиката: password-valid? и password-good?. Оба должны оценивать строку.

password-valid? должен возвращать #t, если строка состоит только из букв и цифр (char-alphabetic? и char-numeric?) и при этом имеет ненулевую длину.

password-good? должен возвращать #t, если строка содержит и буквы, и цифры, а длина строки не меньше восьми символов. И, разумеется, хороший пароль должен быть valid!

Упражнение не проходит проверку — что делать? 😶

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

  • Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет 🤨

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя 🤔

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно 🙄

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Если вы столкнулись с трудностями и не знаете, что делать, задайте вопрос в нашем большом и дружном сообществе