0

I'm having a problem while trying to increment my value of x inside the inner foldl call. I make x equal to shiftValue that's passed in and attempt to increment it whenever I find a #" " or #"*" in the inner foldl call, but the value of x that gets returned is always the same as shiftvalue was when passed in.

The function takes in a tuple of (string, int) where the string will have leading spaces and asterisk chopped off that come before any other characters. Also any spaces or asterisk on the end not followed by any other characters will get chopped off. The int that is passed in is a shiftValue that tracks how many spaces the string was shifted over before getting passed into this function. Whenever I take off a leading space or asterisk I need to increment the shiftValue "x" by one.

The inner foldl call removes asterisks and spaces from the front. The outer foldl call removes them from the back. The asterisks and spaces get removed right, the x value just isn't getting updated.

(*Take string str and get rid of leading and following #"*"s and #" "s. For every 
leading #"*" or #" " removed increment the shiftValue returned in the tuple*)

fun trimStarsOnNode (str, shiftValue) =
    let 
        val x = shiftValue
    in
        ((implode(rev (foldl (fn (cur, a) => 
            if length a = 0 andalso cur = #"*" then a @ []
            else
                if length a = 0 andalso cur = #" " then a @ []
                else a @ [cur]) []  (rev (foldl (fn (cur, a) => 
                    if length a = 0 andalso cur = #"*" then (x = x + 1; a @ [])
                    else
                        if length a = 0 andalso cur = #" " then (x = x + 1; a @ [])
                        else a @ [cur]) [] (explode str)))))), x)
    end;

trimStarsOnNode ("***hello", 3); (* Should print out ("hello", 6) *) but prints out ("hello", 3)

Andrew_CS
  • 2,542
  • 1
  • 18
  • 38

2 Answers2

3

Look at your x - in the beginning of your function, you do:

val x = shiftValue

Then, later, you try to do this:

x = x + 1

Remember, in SML, you can't change the value of a variable (actually, they're just called values in SML, for that reason). x = x + 1 just compares x and x + 1, so the value of the statement x = x + 1 is boolean false.

Tayacan
  • 1,896
  • 11
  • 15
  • I didn't know that you couldn't change the value. Thank you! I got rid of the x = x + 1 and made a separate function to handle changing the shiftValue. – Andrew_CS Jun 30 '13 at 22:11
  • Re: "actually, they're just called values in SML": Per *The Definition of Standard ML (Revised)*, `x` is a "value variable". (It's also a "value identifier", and a "long value identifier", and so on.) The notion that immutable variables wouldn't be "variables" is misguided; it's just that variables in Standard ML are more like variables in math than like variables in many other languages. – ruakh Jul 01 '13 at 07:01
1

As Tayacan says, variables are not mutable in SML. If you want mutability you need to use reference types -- but usually, they are best avoided and it's preferable to stick to functional style.

It's also worth noting that your function is going to be very inefficient (O(n^2)), because of your use of list concatenation and length on every iteration. And it is incorrect, because it will also remove stars in the middle of the string (and then redundantly go over the whole list a second time). Finally, your solution is far too complicated.

FWIW, here is the shortest implementation I can think of, using the Substring library module and the function composition operator o:

fun isStarOrSpace c = (c = #"*" orelse c = #" ")
val trimStars =
    let open Substring
    in string o dropl isStarOrSpace o dropr isStarOrSpace o full end

This does not use your shiftValue because I don't understand what it's supposed to do. You can easily compute the number of removed characters by comparing the old and new string size. That is, your intended function (IIUC) could easily be expressed on top of mine as

fun trimStarsOnNode(s, shift) =
    let val s' = trimStars s in (s', size s - size s' + shift) end

But to be honest, I don't understand what this version would be good for.

Edit: A version that returns the left drop count:

fun trimStars s =
    let
        open Substring
        val ss = dropl isStarOrSpace (dropr isStarOrSpace (full s))
    in
        (string ss, #2(base ss))
    end
Andreas Rossberg
  • 34,518
  • 3
  • 61
  • 72
  • It doesn't remove stars in the middle and I'm not concerned about efficiency, but thanks for the alternate suggestion. – Andrew_CS Jun 30 '13 at 22:08
  • Also if I remove stars from the front the shiftValue changes but not when they're removed from the back, that's why I can't compare the new string length after. – Andrew_CS Jun 30 '13 at 22:10
  • Ah, sorry, you are right about the stars in the middle, I misread the code. Re the shift: I see, but why do you make it a _parameter_? I updated the answer with a version that returns the shift count. – Andreas Rossberg Jul 01 '13 at 07:07