SML:计算字符串中'\n'的数量

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

我是标准 ML 的新手,我有一个问题对于知道如何使用这种语言进行编码的人来说可能是显而易见的。

我有一个原始函数如下:

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
        )       

它返回了一个非常适合在我的程序中显示的字符串。

不幸的是,我还需要这个字符串中'\n'的数量。因此,我考虑制作另一个返回整数的函数,而不是直接从字符串计数(这会很好):

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

但是我的大脑太过投入 procedural/object 思考,我不知道如何用函数式语言执行我想要的。

如果有人可以帮我调试这个函数,因为我真的不知道问题出在哪里:/(或者更好的是,告诉我如何编写一个函数来计算'\n ' 在现有字符串中)

在此先感谢您的帮助!

此致!

编辑

@molbdnilo 你的解决方案似乎很接近解决方案,但我没有设法使用它:/(不幸的是我是一个真正的初学者)。

我有这些功能(已经存在的和你的):

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)

当我调用函数 getBlocked() 以显示创建的字符串时

我正在做如下:

app af (Nodestore.listNodes ())

因为是调用print,所以在任何地方都可以调用。

但是对于你的函数,我需要使用它的返回值,但我没能做到:/

我尝试类似的方法:

res = (app aff Nodestore.listNodes())

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

但不幸的是,它并不像我想的那么简单:/

我收到错误消息:

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

你的函数的主要问题是 res = res + 1 是比较,而不是赋值。

你需要忘记assignment,甚至忘记这样的事情存在。

这里有一种方法可以做你想做的事:

  • 将字符串转为字符列表
  • 过滤它,只保留你感兴趣的角色
  • 取结果的长度

可能看起来像这样:

fun count c s = List.length (List.filter (fn x => x = c) (String.explode s))
fun count_newlines s = count #"\n" s

我不太了解 ML,但我相信你应该使用 foldl,如下所示:

  foldl op+ 0 f

其中 op+ 是加号运算符,0 是初始值,f 是您的自定义函数,可能是匿名函数。

也可以直接对字符串进行递归,而无需先将它们分解成列表:

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)对应取表头,String.extract (s,1,NONE)对应取表尾。我怀疑这比使用 explode 更有效,因为它不需要通过 1 遍来构建列表,然后再通过另一遍来处理列表,尽管这不太可能节省大量甚至明显的时间(除非您正在处理一些非常长的字符串)。

我会在这里推荐 John 的解决方案,并可能将其扩展为 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