0

I am currently writing a code in SML that takes in a string list full of numbers and spaces. The code must combine the numbers up to the space, then create a new list item for the next set of numbers, and so on.

For example, the list ["1", "", "2", "3", "", "4", "5", "6"] would return the list ["1", "23", "456"].

My current non-error attempt includes the code below.

fun conc(L) = 
  case L of
    [] => ""
  | x::xs => x ^ conc(xs);

fun intParse(L as x::xs) = 
  let
    val intStr = conc(L)
  in    
    intStr :: intParse(xs)
  end;

I wanted to write something like the code below, but couldn't without error. This is not exact code, more-so pseudocode that I couldn't figure out.

fun strToInt(nil) = []
  | strToInt(L) = 
    let
      fun conc(y::ys) = 
        if hd(ys) <> "" then y ^ conc(ys)                           
        else y ^ ""
    in
      conc(L) :: strToInt(xs)
    end;

2 Answers 2

0

As you are iterating recursively over a list, you need a base case. You've figured that out. It's [].

The other thing you need is an accumulator or accumulators you can pass along as you iterate. These accumulators can be "hidden" using a locally scoped helper function to which you provide the accumulators in their initial states.

val lst = ["1", "", "2", "3", "", "4", "5", "6"];

fun lstTransform(lst) =
  let
    fun aux([], str, acc) = 
        if str = "" then List.rev(acc)
        else List.rev(str :: acc)
      | aux(x::xs, str, acc) =
        if x = "" andalso str <> "" then 
          aux(xs, "", str :: acc)
        else if x = "" then
          aux(xs, "", acc)
        else 
          aux(xs, str ^ x, acc)
  in
    aux(lst, "", [])
  end;

When you hit the base case, you want to check if the accumulator for the "current" string you've built (str) is empty or not. If it's empty, we just return the accumulator. If not, we need to add that string onto the accumulator.

In either case, because of the way lists are built, they'd be backwards, so we reverse them.

Otherwise you're evaluating the first element in the list and deciding based on that how to update your state for the next iteration.


This type of iteration over a list and accumulation of a new value is exactly what List.foldl is made for. This function lets us get rid of a lot of the boilerplate. We provide a function which works on each element in the list and the accumulator; and the initial state of the accumlator; and the list to work on.

let 
  val (str, acc) =
    List.foldl 
      (fn (x, (str, acc)) => 
         if x = "" andalso str <> "" then ("", str :: acc)
         else if x = "" then ("", acc)
         else (str ^ x, acc))
      ("", [])
      lst
in
  if str = "" then List.rev(acc)
  else List.rev(str :: acc)
end;
Sign up to request clarification or add additional context in comments.

Comments

0

Here is my attempt...

fun squoosh lst =
  let
    fun collect_and_accumulate acc collected =
      fn [] => List.rev (if collected = "" then acc else collected :: acc)
       | (""::ys) => collect_and_accumulate (if collected = "" then acc else collected :: acc) "" ys
       | (x::ys) => collect_and_accumulate acc (collected ^ x) ys; 
  in
    collect_and_accumulate [] "" lst
  end;

Run Code Snippet below to see the result

runCode();
<pre id="code" class="lang-ml s-code-block">
fun squoosh lst =
  let
    fun collect_and_accumulate acc collected =
      fn [] => List.rev (if collected = "" then acc else collected :: acc)
       | (""::ys) => collect_and_accumulate (if collected = "" then acc else collected :: acc) "" ys
       | (x::ys) => collect_and_accumulate acc (collected ^ x) ys; 
  in
    collect_and_accumulate [] "" lst
  end;

  squoosh ["1", "", "2", "3", "", "4", "5", "6"];
</pre>


<script src="https://unpkg.com/@sosml/interpreter@^1.5.0/build/interpreter.min.js"></script>
<script>
  function runCode() {
    try {
      let initialState = Interpreter.getFirstState();
      const code = document.getElementById('code');
      let interpretationResult = Interpreter.interpret(code.innerText, initialState);
      console.log(interpretationResult.state.toString({ stopId: initialState.id + 1 }));
    } catch (error) {
      console.error(error.name, "\n", error.message);
    }
  }
</script>

Comments

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.