S3Q1 · Analyze Sentences¶
⚡ Quick Reference
Function: process_sentence(sentence: str, task: str)
Four tasks, one dispatcher:
def process_sentence(sentence: str, task: str):
words = sentence.split()
if task == "count_words":
return len(words)
elif task == "count_palindrome_words":
return sum(1 for w in words if w == w[::-1])
elif task == "count_words_with_repeated_chars":
return sum(1 for w in words if len(set(w)) < len(w))
elif task == "words_with_max_len":
max_len = max(len(w) for w in words)
return {w for w in words if len(w) == max_len}
Key rules:
- w == w[::-1] → palindrome check
- len(set(w)) < len(w) → has repeated characters (set removes duplicates, so if shorter than original, there were repeats)
- words_with_max_len returns a set, not a list
Problem Statement¶
Problem
Write a function process_sentence(sentence, task) that performs one of four analyses on a sentence depending on task:
task |
Returns |
|---|---|
"count_words" |
Total number of words |
"count_palindrome_words" |
Number of palindrome words |
"count_words_with_repeated_chars" |
Number of words with at least one repeated character |
"words_with_max_len" |
A set of the longest word(s) |
Examples:
sentence = "level noon civic radar something"
task = "count_words"
5
sentence = "level noon civic radar something"
task = "count_palindrome_words"
4
sentence = "hello world programming fun and interesting"
task = "count_words_with_repeated_chars"
3
sentence = "hello world programming fun and interesting"
task = "words_with_max_len"
{'interesting', 'programming'}
Breaking down each task¶
Task 1 - count_words¶
Simply count the words after splitting:
sentence.split() splits on any whitespace and handles multiple spaces gracefully.
Task 2 - count_palindrome_words¶
A word is a palindrome if it reads the same forwards and backwards:
| Word | w[::-1] |
Palindrome? |
|---|---|---|
"level" |
"level" |
✅ Yes |
"noon" |
"noon" |
✅ Yes |
"civic" |
"civic" |
✅ Yes |
"radar" |
"radar" |
✅ Yes |
"something" |
"gnihtemos" |
❌ No |
Count: 4
[::-1] for palindrome check
w[::-1] reverses the string. If the reversed string equals the original, it's a palindrome. This is the cleanest one-liner palindrome check in Python.
Task 3 - count_words_with_repeated_chars¶
A word has repeated characters if any character appears more than once. The elegant way to check: convert to a set and compare lengths.
If set(w) is shorter than w, at least one character was a duplicate.
| Word | len(w) |
len(set(w)) |
Repeated? |
|---|---|---|---|
"hello" |
5 | 4 ({h,e,l,o}) |
✅ Yes (l repeated) |
"world" |
5 | 5 ({w,o,r,l,d}) |
❌ No |
"programming" |
11 | 8 ({p,r,o,g,a,m,i,n}) |
✅ Yes (r,g,m repeated) |
"fun" |
3 | 3 | ❌ No |
"and" |
3 | 3 | ❌ No |
"interesting" |
11 | 7 ({i,n,t,e,r,s,g}) |
✅ Yes |
Count: 3
set length trick
len(set(w)) < len(w) is the most Pythonic way to check for duplicates in any sequence. A set can only hold unique elements - if the set is smaller than the original, there were duplicates.
Task 4 - words_with_max_len¶
Find the length of the longest word, then collect all words matching that length into a set:
For "hello world programming fun and interesting":
| Word | Length |
|---|---|
"hello" |
5 |
"world" |
5 |
"programming" |
11 ← max |
"fun" |
3 |
"and" |
3 |
"interesting" |
11 ← max |
max_len = 11 → result: {'programming', 'interesting'}
The result is a set - order is not guaranteed, and that is expected.
Solution approaches¶
def process_sentence(sentence: str, task: str):
words = sentence.split()
if task == "count_words":
return len(words)
elif task == "count_palindrome_words":
count = 0
for w in words:
if w == w[::-1]:
count += 1
return count
elif task == "count_words_with_repeated_chars":
count = 0
for w in words:
if len(set(w)) < len(w):
count += 1
return count
elif task == "words_with_max_len":
max_len = max(len(w) for w in words)
result = set()
for w in words:
if len(w) == max_len:
result.add(w)
return result
def process_sentence(sentence: str, task: str):
words = sentence.split()
if task == "count_words":
return len(words)
elif task == "count_palindrome_words":
return sum(1 for w in words if w == w[::-1])
elif task == "count_words_with_repeated_chars":
return sum(1 for w in words if len(set(w)) < len(w))
elif task == "words_with_max_len":
max_len = max(len(w) for w in words)
return {w for w in words if len(w) == max_len}
sum(1 for w in words if condition) is the idiomatic way to count items satisfying a condition - cleaner than maintaining a counter variable.
def is_palindrome(w):
return w == w[::-1]
def has_repeated_chars(w):
return len(set(w)) < len(w)
def process_sentence(sentence: str, task: str):
words = sentence.split()
if task == "count_words":
return len(words)
elif task == "count_palindrome_words":
return sum(1 for w in words if is_palindrome(w))
elif task == "count_words_with_repeated_chars":
return sum(1 for w in words if has_repeated_chars(w))
elif task == "words_with_max_len":
max_len = max(len(w) for w in words)
return {w for w in words if len(w) == max_len}
The problem explicitly allows helper functions. Extracting is_palindrome and has_repeated_chars makes the main function easier to read and each helper independently testable.
def process_sentence(sentence: str, task: str):
words = sentence.split()
def count_words():
return len(words)
def count_palindrome_words():
return sum(1 for w in words if w == w[::-1])
def count_words_with_repeated_chars():
return sum(1 for w in words if len(set(w)) < len(w))
def words_with_max_len():
max_len = max(len(w) for w in words)
return {w for w in words if len(w) == max_len}
dispatch = {
"count_words": count_words,
"count_palindrome_words": count_palindrome_words,
"count_words_with_repeated_chars": count_words_with_repeated_chars,
"words_with_max_len": words_with_max_len,
}
return dispatch[task]()
Maps task strings to inner functions and calls the right one. Avoids a long if-elif chain - useful when the number of tasks grows large. Advanced but clean.
def process_sentence(sentence: str, task: str):
words = sentence.split()
if task == "count_words":
return len(words)
elif task == "count_palindrome_words":
return len(list(filter(lambda w: w == w[::-1], words)))
elif task == "count_words_with_repeated_chars":
return len(list(filter(lambda w: len(set(w)) < len(w), words)))
elif task == "words_with_max_len":
max_len = max(map(len, words))
return set(filter(lambda w: len(w) == max_len, words))
filter(lambda w: condition, words) replaces the comprehension - keeps only words satisfying the condition. len(list(...)) counts them. For words_with_max_len, map(len, words) computes all lengths in one pass, and filter selects the longest words. Pure functional style throughout.
Key takeaways¶
w == w[::-1] for palindromes
Reversing with [::-1] and comparing is the one-liner palindrome check. Works for any sequence - strings, lists, tuples.
len(set(w)) < len(w) for duplicates
Converting to a set removes duplicates. If the set is smaller than the original, duplicates existed. Works for any iterable, not just strings.
sum(1 for x in iterable if cond)
The idiomatic way to count items satisfying a condition. Equivalent to a counter loop but cleaner. Memorise this pattern - it appears constantly.