Search code examples
algorithmfunctional-programmingsmlsmlnj

SML : Count the number of '\n' in a String


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

Solution

  • 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