Jump to content
  • Advertisement
Sign in to follow this  

I need functional language algorithm help

This topic is 1933 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

First let me say this, this problem is from How To Design Programs, 2nd Edition (Chapter 4, Section 4.5.1).
Second, this is not homework. I'm self-learning.
Now, I've made two attempts at this problem. First and Second. My issue is that I know I'm doing something wrong. But I don't know what it is. My process of going through the problem is jacked somehow. In the first attempt, the code looks like right. I'm using recursion, following the steps the book has laid out (to the best of my understanding). But the result produced is the answer plus junk. In the second attempt, there is some recursion and helper functions. And the answers are correct. However, the code feels .... wrong. As if I didn't do it the Scheme way. It's like writing C with classes and not idiomatic C++. It's right, but it's wrong.
So any tips, hints, pointing in the right direction, would be greatly appreciated.

Share this post

Link to post
Share on other sites
Your second (working) solution gets messy after it gets to insert-everywhere/in-one-word.
The approach is fine, you just haven't written it neatly.

- Your argument names are cryptic and sometimes in an illogical order.
- You write extra functions that obscure rather than clarify. For instance, I have no idea what "move-letters" is supposed to do and what arguments it's supposed to take unless I read the code. And when I read the code, I find it's just (append x (list y)). Stuff like this is better inlined.
- You should always nest definitions that are only intended to be used inside another function. It makes the structure of the code far easier to comprehend, plus it often further simplifies code by allowing you to drop any function arguments that are already defined in the outer scope.

I wrote an exact Scala equivalent to your insert-everywhere/in-one-word function but fixed all of the above. I think it looks fine with these changes. Note how the recursive helper has one less argument than yours, and one function even turned into a local definition with no arguments.
def insertEverywhere(c: Char, string: List[Char]) = {
  def recInsert(stringBefore: List[Char], stringAfter: List[Char]): List[List[Char]] = {
    val newWord = stringBefore ::: (c :: stringAfter)
    if (stringAfter.isEmpty) List(newWord)
    else newWord :: recInsert(stringBefore ::: List(stringAfter.head), stringAfter.tail)
  recInsert(Nil, string)
Direct Racket translation: :: is cons, ::: is append, head is first, tail is rest, Nil is empty, val is define. Edited by Yrjö P.

Share this post

Link to post
Share on other sites

And the scheme/racket version of your code:


(define (insert-everywhere c string)
  (local [(define (recursive-insert string-before string-after)
            (local [(define new-word (append string-before (cons c string-after)))]
                [(empty? string-after) (list new-word)]
                [else (cons new-word 
                              (append string-before (list (first string-after))) 
                              (rest string-after)))])))]
  (recursive-insert empty string)))

(insert-everywhere "w" (list "e" "a" "r"))
Edited by Alpha_ProgDes

Share this post

Link to post
Share on other sites

While your code is certainly correct and written in functional style, I would suggest you try to go one step further. Now, it seems that you are 'inlining' different steps into one big step. Functional programming has the advantage of giving the programmer the possibility to clearly distinguish between different 'levels' of operation, each following a certain pattern.


For example, let's say I have a list of numbers and want to take the sum of the squares of all uneven numbers. The following Haskell-code (I picked Haskell because even if you are not familiar with it, I believe it is more readable than Scheme, as long as I stay away from some of the more arcane notations) does this in a straightforward manner:


bizarreSum :: [Integer] -> Integer
bizarreSum []     = 0                            -- empty list case
bizarreSum (x:xs)                                -- cons case (head = x, tail = xs)
    | odd x       = x * x + bizarreSum xs        --    head is odd
    | otherwise   = bizarreSum xs


But one can distinguish three different steps in this algorithm: first, we filter out the even numbers, then we square every number, and finally, we summate. This can be written as

bizarreSum' :: [Integer] -> Integer
bizarreSum' = foldl (+) 0 . map sqr . filter odd




The dot chains the operations together: filtering, mapping, folding. Filter, map and foldl are three well-known patterns, they are in a way higher-level functional loops. In my opinion, it is preferable to try to find these patterns and try to implement your algorithms using them, as you work on a higher level of abstraction.


Permutation can then be implemented as a series of mapping operations. My Haskell implementation looks like this:


split :: [a] -> [([a], a, [a])]
split xs = [ (take n xs, xs !! n, drop (n+1) xs)
           | n <- [ 0 .. length xs - 1 ] ]

permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations xs = do (prefix, x, postfix) <- split xs
                     p <- permutations (prefix ++ postfix)
                     return (x : p)


The permutations function first splits a list [a(1), a(2), ..., a(n)] up in three parts: a prefix list [a(1)..a(i-1)], an element x=a(i), and a postfix list [a(i+1)..a(n)], for every possible i (this is a map). Then for all permutations p of prefix++postfix (again a map) it prepends x=a(i) to the front of p. So, this is akin to a nested loop, or in our case, a map within a map. In above Haskell code, you can interpret "x <- xs" as "for each x in xs" (it uses monads, which are  a very powerful concept, and I love to explain people about them, but now it is a bit out of scope of this post).


In your first attempt, insert-everywhere/in-all-words seems to be wrong:

(insert-everywhere/in-all-words 1 '((2 3)))
'((1 2 3) (1 3 2) (2 1 3) (3 2 1) (3 1 2) (2 3 1))

I don't think it is supposed to generate all permutations of (2 3) in this example. I didn't take the time to inspect your code thoroughly, but I did notice a lot of conds and recursion, which means that you are constantly reimplementing functional building blocks, map in your case.


Final note: it is true that bizarreSum' is less memory-efficient than bizarreSum, as each time a new list must be created in memory (however, this does not apply to Haskell as it is a nonstrict language, but in most other languages, this could be an actual issue). However, would performing your own inlining not be a case of premature optimisation? If it is your intention to practice functional programming, I believe it is more important to focus on learning to think on a higher level than to be efficient.

Edited by SamLowry

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!