006: Palindrome Check
Difficulty: Beginner Category: Foundation Concept: Check if a string/list reads the same forwards and backwards Key Insight: Rust's `chars().rev()` iterator makes palindrome checking elegant; both languages compare with reversed version.// 006: Palindrome Check
// Approach 1: Reverse and compare (allocating)
fn is_palindrome_rev(s: &str) -> bool {
let reversed: String = s.chars().rev().collect();
s == reversed
}
// Approach 2: Iterator comparison (zero allocation)
fn is_palindrome_iter(s: &str) -> bool {
s.chars().eq(s.chars().rev())
}
// Approach 3: Case-insensitive, alphanumeric only
fn is_palindrome_clean(s: &str) -> bool {
let clean: Vec<char> = s
.chars()
.filter(|c| c.is_alphanumeric())
.map(|c| c.to_lowercase().next().unwrap())
.collect();
clean.iter().eq(clean.iter().rev())
}
fn main() {
let words = ["racecar", "hello", "abba", "A man, a plan, a canal: Panama"];
for w in &words {
println!("'{}' palindrome? {}", w, is_palindrome_rev(w));
}
println!("Clean palindrome: {}", is_palindrome_clean("Race Car"));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_palindrome_rev() {
assert!(is_palindrome_rev("racecar"));
assert!(is_palindrome_rev("abba"));
assert!(!is_palindrome_rev("hello"));
assert!(is_palindrome_rev(""));
assert!(is_palindrome_rev("a"));
}
#[test]
fn test_palindrome_iter() {
assert!(is_palindrome_iter("racecar"));
assert!(!is_palindrome_iter("abc"));
}
#[test]
fn test_palindrome_clean() {
assert!(is_palindrome_clean("A man, a plan, a canal: Panama"));
assert!(is_palindrome_clean("Race Car"));
assert!(!is_palindrome_clean("hello world"));
}
}
(* 006: Palindrome Check *)
(* Approach 1: Reverse and compare *)
let is_palindrome_rev s =
let chars = List.of_seq (String.to_seq s) in
chars = List.rev chars
(* Approach 2: Index-based comparison *)
let is_palindrome_idx s =
let n = String.length s in
let rec check i =
if i >= n / 2 then true
else if s.[i] <> s.[n - 1 - i] then false
else check (i + 1)
in
check 0
(* Approach 3: Case-insensitive, alphanumeric only *)
let is_palindrome_clean s =
let clean =
s
|> String.lowercase_ascii
|> String.to_seq
|> Seq.filter (fun c ->
(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
|> List.of_seq
in
clean = List.rev clean
(* Tests *)
let () =
assert (is_palindrome_rev "racecar");
assert (is_palindrome_rev "abba");
assert (not (is_palindrome_rev "hello"));
assert (is_palindrome_rev "");
assert (is_palindrome_rev "a");
assert (is_palindrome_idx "racecar");
assert (not (is_palindrome_idx "abc"));
assert (is_palindrome_clean "A man, a plan, a canal: Panama");
assert (is_palindrome_clean "Race Car");
assert (not (is_palindrome_clean "hello world"));
Printf.printf "โ All tests passed\n"
๐ Detailed Comparison
Core Insight
A palindrome reads the same forwards and backwards. The simplest check: reverse and compare. Both languages make this a one-liner, but the underlying data structures differ (OCaml string vs Rust UTF-8 String).
OCaml Approach
- Convert string to char list, reverse, compare
- Or use index-based comparison from both ends
- `String.to_seq` + `List.of_seq` for char extraction
Rust Approach
- `s.chars().rev().collect::<String>() == s`
- Or `s.chars().eq(s.chars().rev())` โ no allocation
- `.chars()` handles UTF-8 correctly
Comparison Table
| Feature | OCaml | Rust | ||
|---|---|---|---|---|
| Reverse string | `String.to_seq \ | > List.of_seq \ | > List.rev` | `s.chars().rev()` |
| Compare | `=` structural equality | `==` or `.eq()` | ||
| UTF-8 | Byte-based strings | `.chars()` for Unicode | ||
| Zero-alloc check | Index loop | `.chars().eq(s.chars().rev())` |