exercism

Exercism - Proverb

This post shows you how to get Proverb exercise of Exercism.

Stevinator Stevinator
7 min read
SHARE
exercism dart flutter proverb

Preparation

Before we click on our next exercise, let’s see what concepts of DART we need to consider

Proverb Exercise

So we need to use the following concepts.

List.generate

The List.generate method creates a list of a specified length by calling a function for each index. It’s perfect for creating lists dynamically based on calculations.

void main() {
  // Generate list of strings
  List<String> items = List.generate(3, (i) => 'Item ${i + 1}');
  print(items); // [Item 1, Item 2, Item 3]
  
  // Generate with calculations using adjacent elements
  List<String> pairs = ['a', 'b', 'c'];
  List<String> combined = List.generate(
    pairs.length - 1,
    (i) => '${pairs[i]} and ${pairs[i + 1]}'
  );
  print(combined); // [a and b, b and c]
}

Spread Operator

The spread operator (...) allows you to expand a list into individual elements. This is useful for combining multiple lists or adding elements to a list.

void main() {
  List<String> list1 = ['a', 'b'];
  List<String> list2 = ['c', 'd'];
  
  // Combine lists using spread
  List<String> combined = [...list1, ...list2];
  print(combined); // [a, b, c, d]
  
  // Add elements to existing list
  List<String> result = [
    ...List.generate(2, (i) => 'Item $i'),
    'Final item'
  ];
  print(result); // [Item 0, Item 1, Final item]
}

String Interpolation

String interpolation allows you to embed expressions inside strings using ${expression} or $variable for simple variable names.

void main() {
  String item1 = 'nail';
  String item2 = 'shoe';
  
  // Simple interpolation
  String sentence = 'For want of a $item1 the $item2 was lost.';
  print(sentence); // "For want of a nail the shoe was lost."
  
  // Expression interpolation
  int index = 0;
  String message = 'Item at index ${index + 1}';
  print(message); // "Item at index 1"
}

String Joining

The join method combines all elements of a list into a single string, with an optional separator between elements.

void main() {
  List<String> lines = [
    'Line 1',
    'Line 2',
    'Line 3'
  ];
  
  // Join with newline
  String text = lines.join('\n');
  print(text);
  // Line 1
  // Line 2
  // Line 3
  
  // Join with space
  String sentence = lines.join(' ');
  print(sentence); // "Line 1 Line 2 Line 3"
}

List Indexing

You can access list elements by their index. Be careful to check bounds to avoid index out of range errors.

void main() {
  List<String> items = ['nail', 'shoe', 'horse'];
  
  // Access by index
  print(items[0]); // 'nail'
  print(items[1]); // 'shoe'
  
  // Access adjacent elements
  for (int i = 0; i < items.length - 1; i++) {
    print('${items[i]} -> ${items[i + 1]}');
  }
  // nail -> shoe
  // shoe -> horse
}

Conditional Logic

You can use if statements to handle edge cases, such as empty lists or special conditions.

void main() {
  List<String> items = [];
  
  // Check if list is empty
  if (items.isEmpty) {
    print('List is empty');
  } else {
    print('List has ${items.length} items');
  }
  
  // Early return pattern
  String process(List<String> data) {
    if (data.isEmpty) return '';
    // Process data...
    return 'result';
  }
}

List Length

The length property returns the number of elements in a list. It’s useful for loops and boundary checks.

void main() {
  List<String> items = ['a', 'b', 'c'];
  
  print(items.length); // 3
  
  // Generate one less than length
  int count = items.length - 1;
  print(count); // 2
  
  // Iterate up to length - 1
  for (int i = 0; i < items.length - 1; i++) {
    print(items[i]);
  }
}

Introduction

For want of a horseshoe nail, a kingdom was lost, or so the saying goes.

Given a list of inputs, generate the relevant proverb. For example, given the list ["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"], you will output the full text of this proverbial rhyme:

For want of a nail the shoe was lost.
For want of a shoe the horse was lost.
For want of a horse the rider was lost.
For want of a rider the message was lost.
For want of a message the battle was lost.
For want of a battle the kingdom was lost.
And all for the want of a nail.

Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content. No line of the output text should be a static, unchanging string; all should vary according to the input given.

What is “For Want of a Nail”?

“For Want of a Nail” is a proverbial rhyme that demonstrates how small, seemingly insignificant events can lead to major consequences through a chain of cause and effect. The rhyme traces a series of events from the loss of a horseshoe nail to the loss of a kingdom, illustrating the butterfly effect in action.

The proverb has been used for centuries to teach the importance of attention to detail and the interconnectedness of events.

— Wikipedia

How does the proverb generation work?

To generate the proverb from a list of items:

  1. Generate pairs: For each adjacent pair of items in the list, create a line in the format “For want of a [first] the [second] was lost.”
  2. Add final line: Add a concluding line “And all for the want of a [first item].”
  3. Join lines: Combine all lines with newline characters

For example, with ["nail", "shoe", "horse"]:

  • Pairs:
    • “For want of a nail the shoe was lost.”
    • “For want of a shoe the horse was lost.”
  • Final line: “And all for the want of a nail.”
  • Result: Three lines joined with newlines

The key insight is that we need n-1 lines for n items (one line for each adjacent pair), plus one final line referencing the first item.

Solution

class Proverb {
  String recite(List<String> pieces) {
    if (pieces.isEmpty) return '';
    
    return [
      ...List.generate(
        pieces.length - 1,
        (i) => 'For want of a ${pieces[i]} the ${pieces[i + 1]} was lost.',
      ),
      'And all for the want of a ${pieces[0]}.',
    ].join('\n');
  }
}

Let’s break down the solution:

  1. if (pieces.isEmpty) return '' - Handles the edge case where the input list is empty by returning an empty string immediately

  2. List.generate(pieces.length - 1, ...) - Creates a list of n-1 elements where n is the length of the input list:

    • For each index i from 0 to pieces.length - 2, generates a string
    • The string format is: 'For want of a ${pieces[i]} the ${pieces[i + 1]} was lost.'
    • This creates one line for each adjacent pair of items
  3. Spread operator ... - Expands the generated list into individual elements within the outer list

  4. Final line - Adds 'And all for the want of a ${pieces[0]}.' to reference the first item in the list

  5. .join('\n') - Combines all elements of the list into a single string, separated by newline characters

The solution elegantly handles lists of any length by generating the appropriate number of lines dynamically, using string interpolation to insert the actual item names, and joining them with newlines to create the final proverb.


A video tutorial for this exercise is coming soon! In the meantime, check out my YouTube channel for more Dart and Flutter tutorials. 😉

Visit My YouTube Channel
Stevinator

Stevinator

Stevinator is a software engineer passionate about clean code and best practices. Loves sharing knowledge with the developer community.