1
votes

I am attempting to write a clojure version of lecture 6.7 from Martin Odersky"s functional progamming course. The original was written in Scala.

The concept is to take a phone number (as a string) and produce a list of possible phrases given a dictionary of words.

So, translate("7225247386") would produce, amongst others, the phrase "Scala is fun"

Here's a Gist with the full Scala version.

Here's my attempt using Clojure

The problem occurs with the final 'encode' function The Scala version is

def encode(number: String): Set[List[String]] =
if (number.isEmpty) Set(List())
else {
    for {
        split <- 1 to number.length
        word <- wordsForNum(number take split)
        rest <- encode(number drop split)
        } yield word :: rest
}.toSet

With my Clojure version

    (defn encode
      "Takes a number as a string. Returns all the different ways to encode that number as a list of words"
  [number]
  (if (empty? number)
    (vector)             
    (for [split (range 0 (count number))      
          word  (words-from-number (subs number 0 split)) 
          remainder (encode (subs number split))]    
      (concat word remainder))))

The problem seems to be that strings keep getting converted to lists of characters; that's why I used (subs number 0 split) rather than (take split number)

A version of the above which just finds the first word seems to be on the right track...

    (defn encode
  [number]
  (for [split (range 0 (count number))
        word (words-from-number (subs number 0 split))]
    word))

(encode "7225247386") => ("pack" "rack" "sack" "Scala")

...but adding in the recursive calls to 'encode' with the remaining digit-string just returns an empty list.

Any advice would be gratefully appreciated.

Thank you.

1

1 Answers

1
votes

You can use clojure.string/join to turn the sequence of characters back into a string.

There are a few bits in your code that are slightly different from the original Scala version:

  • The Scala version's range starts from 1, yours is from 0
  • The Scala version's if->true returns a nested collection
  • You are concatenating the two sequences together, not "consing" onto the list

I created a modified version that might not be exactly correct (didn't check it against the Scala version's output), but hopefully will help you:

(defn encode
  "Takes a number as a string. Returns all the different ways to encode that number as a list of words"
  [number]
  (if (empty? number)
    #{()}
    (for [split (range 1 (inc (count number)))
          word  (words-from-number (join (take split number)))
          remainder (encode (join (drop split number)))] 
      (cons word remainder))))

Possibly of interest I've implemented the same translation a while ago: https://gist.github.com/ponzao/6095989