简体   繁体   中英

SML: function with multiple outputs

I´ma newbie in SML and I´d like to update my function so that it has two outputs: a list AND 1 or 0. The function was proposed here: SML: Remove the entry from the List . It returns an updated list without a row that contains ´elem´.

fun removeElem elem myList = filter (fn x => x <> elem) myList

The function should return a new list AND 1, if a raw has been deleted. Otherwise, it should return an old list AND 0.

Any hint or example is highly appreciated. Thanks.

Note that all SML functions take a single input and return a single output. Instead, think of returning a tuple containing the new list and a flag indicating whether any elements were removed. One possibility is to use a couple of functions from the standard basis to test whether elem is in myList and build up a tuple consisting of that and the results from the filter shown in the question. The test might look like:

Option.isSome (List.find (fn x => x = elem) myList)

There are more concise ways to write that, but it shows the idea. Note that it returns a bool instead of an int ; this is more precise, so I won't convert to the integers requested in the question.

A drawback of the above is that it requires traversing the list twice. To avoid that, consider the type that the function must return: a tuple of a list without elem and a flag showing whether any elem s have been removed. We can then write a function that take a new value and a (valid) tuple, and returns a valid tuple. One possibility:

fun update(x, (acc, flag)) = if x = elem then (acc, true) else (x :: acc, flag)  

We can then apply update to each element of myList one-by-one. Since we want the order of the list to stay the same, apart from the removed elements, we should work through myList from right to left, accumulating the results into an initially empty list. The function foldr will do this directly:

foldr update ([], false) myList

However, there is a lot of logic hidden in the foldr higher-order function.

To use this as a learning exercise, I'd suggest using this problem to implement the function in a few ways:

  • as a recursive function
  • as a tail-recursive function
  • using the higher order functions foldl and foldr

Understanding the differences between these versions will shed a lot of light on how SML works. For each version, let the types guide you.

As has been stated in some of your previous questions; Returning 0 or 1 as an indicator for what happened is a really bad design, as you don't get any guarantees from the types, whether or not you will get -42 as the result. Since you are working with a strongly typed language, you might as well use this to your advantage:

  1. The most obvious thing to do instead would be to return a boolean, as that is actually what you are emulating with 0 and 1. In this case you could return the pair (true, modified_list) or (false, original_list) .
  2. Since you want to associate some data with the result, there is another -- perhaps, for some, less -- obvious thing to do; Return the result as an option, indication a change in the list as SOME modified_list and indication no change as NONE .

In either case you would have to "remember" whether or not you actually removed any elements from the original list, and thus you can't use the filter function. Instead you would have to do this for yourself using somewhat the same code as you originally posted.

One way would be like this

fun removeElem _ [] = (false, [])
  | removeElem elem (x::xs) =
    let
      val (b, xs') = removeElem elem xs
    in
      if elem = x then
        (true, xs')
      else
        (b, x::xs')
    end

Another way would be to use a accumulator parameter to store the resulting list

fun removeElem elem xs =
    let
      fun removeElem' [] true res = SOME (rev res)
        | removeElem' [] false _ = NONE
        | removeElem' (x::xs) b res =
          if elem = x then
            removeElem' xs true res
          else
            removeElem' xs b (x::res)
    in
      removeElem' xs false []
    end

Since the solution is being built in the reverse order, we reverse the result just before we return it. This makes sure that we don't have to use the costly append operation when adding elements to the result list: res @ [x]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM