1

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.

0

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

I did originally use clojure.string/join but it just felt wrong to join/split/join/split... Scala's strings seem to behave as sequences so using take and join return a string but in clojure they return a list of characters, so I felt using subs would be more appropriate. Your modified version is, in the end, identical to my original attempt (which is not the posted version), before all the tweeking, with the important difference that you return an empty set when the string is exhausted. To be fair that's what the Scala version does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.