3

I'm quite new in Standard ML and I have a question that will probably be obvious to someone who knows how to code in this language.

I've got an original function as follow :

fun getBlocked w =
case BlockingMgr.listBlockedSuccessors w
    of nil => ""
    | ws =>
        concat (
            List.map
                (fn (r, w') => (
                    "v r1 w"
                    ^
                      Int.toString (Node.getId w))
                    ^ " w"
                    ^ (Int.toString (Node.getId (w')))
                    ^ "\n"
                )
            ws
        )       

Which is returning me a string perfect to display in my program.

Unfortunately, I need also the number of '\n' in this string. So instead of counting directly from the string (which would be nice) I thought about making another function which is returning an integer :

fun getNbBlocked w =
    let res = 0;
    case BlockingMgr.listBlockedSuccessors w
        of nil => res
        | ws =>
            List.map
                (fn (r, w') => (
                    res = res+1
                )
            ws  

But my brain is too much into the procedural/object thinking and I don't know how to perform what I want in a functional language.

If someone could help me to debug this function because I don't really know what is the problem :/ (or even better, helping me by telling me how to code a function to count the number of '\n' in the existing string)

Thanks in advance for your help !

Best Regards !

EDIT

@molbdnilo Your solution seems to be close to the solution, but I don't manage to use it :/ (I'm a true beginner unfortunately).

I've got theses functions (the already existing one and yours) :

val res = 0

fun getBlocked w =
    case BlockingMgr.listBlockedSuccessors w
        of nil => ""
        | ws =>
            concat (
                List.map
                    (fn (r, w') => (
                        "v r1 w"
                        ^
                        Int.toString (Node.getId w))
                        ^ " w"
                        ^ (Int.toString (Node.getId (w')))
                        ^ "\n"
                        )
                    ws
                )       

fun count c s = List.length (List.filter (fn x => x = c) (String.explode s));;

fun count_newlines s = count #"\n" s;;

fun af w = print(getBlocked w)

fun aff w = count_newlines(getBlocked w)

When I'm calling the function getBlocked() in order to display the created string

I'm doing as follow :

app af (Nodestore.listNodes ())

Because it's calling print, so it can be call anywhere.

But for your function, I need to use the returned value of it and I don't manage to do it :/

I try something like :

res = (app aff Nodestore.listNodes())

(* We then, display the number of edges *)
print (Int.toString (res));

But it's unfortunately, not as simple as I thought :/

I get the error message :

Error: src/output/modeloutput.sml 101.11.
  Function applied to incorrect argument.
    expects: _ -> [unit]
    but got: _ -> [int]
    in: app aff
Error: src/output/modeloutput.sml 101.11.
  Function applied to incorrect argument.
    expects: [Node.node list]
    but got: [unit -> Node.node list]
    in: (app aff) Nodestore.listNodes
Error: src/output/modeloutput.sml 101.11.
  Function not of arrow type.
    function: [unit]
    in: ((app aff) Nodestore.listNodes) ()
      pre codegen raised in 2.79 + 1.50 (35% GC)
      pre codegen raised: Fail
   Compile SML raised in 2.79 + 1.50 (35% GC)
   Compile SML raised: Fail
Valentin Montmirail
  • 2,594
  • 1
  • 25
  • 53

4 Answers4

3

The main problem with your function is that res = res + 1 is a comparison, not an assignment.

You need to unlearn assignment, even forget that such a thing exists.

Here's one way to do what you want:

  • Turn the string into a list of characters
  • Filter it and keep only the character you're interested in
  • Take the length of the result

which could look like this:

fun count c s = List.length (List.filter (fn x => x = c) (String.explode s))
fun count_newlines s = count #"\n" s
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
2

It is also possible to do recursion over strings directly without first exploding them into a list:

fun countChar c "" = 0
|   countChar c s = let val ch = String.sub (s,0) in
    (if ch = c then 1 else 0) + countChar c (String.extract (s,1,NONE))
end;

String.sub (s,0) corresponds to taking the head of a list and String.extract (s,1,NONE) corresponds to taking the tail. I suspect that this is more efficient than using explode since it doesn't require 1 pass to build up a list and then another to process the list, though this is unlikely to be a significant or even noticeable time savings (unless you are processing some really long strings).

John Coleman
  • 51,337
  • 7
  • 54
  • 119
2

I would recommend John's solution here and perhaps extend it to be tail-recursive:

fun countChar c s =
    let val len = String.size s
        fun cc i n = if i = len then n
                     else cc (i+1) (if String.sub (s, i) = c then n+1 else n)
    in cc 0 0
    end
sshine
  • 15,635
  • 1
  • 41
  • 66
0

I don't know ML well but I believe you should use foldl, something like this:

  foldl op+ 0 f

Where op+ is the plus operator, 0 is initial value and the f is your custom, probably anonymous function.

Incerteza
  • 32,326
  • 47
  • 154
  • 261