English 中文(简体)
递归表达式,其唯一的基本情况是异常[Context:Reading from files in OCaml]
原标题:Recursive expression whose only base case is an exception [Context: Reading from files in OCaml]

编辑:忽略这个问题!请参阅下面的评论

我想要一个OCaml表达式,它传递一个文件(作为“in_channel”),然后逐行读取文件,进行一些处理,直到最后,然后返回处理结果。

我写了这个测试:

let rec sampler_string file string_so_far =
    try 
        let line = input_line file in
        let first_two_letters = String.sub line 0 2 in
        sampler_string file (string_so_far ^ first_two_letters)
    with End_of_file -> string_so_far;;

let a = sampler_string (open_in Sys.argv.(1)) "";;

(这里的“做一些处理”是将每行的前两个字符添加到一个连续的计数中,其想法是在末尾返回一个包含每行前两个字母的字符串。)

这不起作用:OCaml认为“sampler_string”产生的是unit类型的东西,而不是string类型的东西。(当我稍后尝试将结果用作字符串时,会出现困难。)我认为这个问题是因为唯一的基本情况发生在异常(End_of_file)中。

因此,一个具体问题和一个一般问题:

  1. Is there a way to fix this code, by explicitly telling OCaml to expect that the result of sampler_string should be a string?
  2. Is there some standard, better syntax for a routine which reads a file line by line to the end, and returns the result of line-by-line processing?
最佳回答

正如Damien Pollet所说,您的sampler_string函数在我的机器ocaml v3.12.0上也能很好地编译(并正确运行)。但是,我将回答您的问题:

  1. 您可以使用:运算符指定函数/值的类型。例如,下面是带有注释的函数的类型。您会注意到,返回类型被放在函数声明的最后。

    let rec sampler_string (file : in_channel) (string_so_far : string) : string = ...
    
  2. 我不知道是否有更好的方法逐行读取文件。被迫通过异常处理文件结尾当然是一件痛苦的事情这是一篇关于主题的博客文章,尽管这里提供的功能是将文件读取到行列表中。另一个邮件列表版本

有几个小问题:

  1. You don t need to use ;; to separate function/value definitions, ocamlc can figure it out from whitespace.
  2. You should close your file sockets.
  3. String.sub will throw an exception if your file has a line with less than 2 characters.
问题回答

风格的一个要点是避免在异常处理程序内部进行递归调用。这样的调用在尾部位置是而不是,因此您将使用足够大的文件来破坏堆栈。请改用此模式:

let rec sampler_string file string_so_far =
  match try Some (input_line file) with End_of_file -> None with
  | Some line ->
      let first_two_letters = String.sub line 0 2 in
      sampler_string file (string_so_far ^ first_two_letters)
  | None -> string_so_far

当然,更好的函数策略是抽象掉递归模式:

let rec fold_left_lines f e inch =
  match try Some (input_line inch) with End_of_file -> None with
  | Some line -> fold_left_lines f (f e line) inch
  | None -> e

因为“用文件的行做事情”本身就是一个通常有用的操作(计算行、计算单词、查找最长的行、解析等都是该模式的特定实例)。那么您的功能是:

let sampler_string file string_so_far =
  fold_left_lines (fun string_so_far line ->
      let first_two_letters = String.sub line 0 2 in
      string_so_far ^ first_two_letters)
    string_so_far file

正如Matias所指出的,首先重要的是将递归调用移到try/with表达式之外,以便对其进行尾调用优化。

但是,有一个半标准的解决方案:使用Batteries Included。Batteries提供了迭代概念的抽象Enums。然后,它的IO基础设施提供了BatIO.lines_of函数,该函数返回文件行的枚举。因此,整个函数可以变成这样:

fold (fun s line -> s ^ String.sub line 0 2) "" (BatIO.lines_of file)

枚举将在文件用尽或垃圾回收时自动关闭该文件。

使用缓冲区可以使代码更加高效(避免重复串联):

let buf = Buffer.create 2048 in
let () = iter (fun line -> Buffer.add_string buf (String.sub line 0 2))
  (BatIO.lines_of file) in
Buffer.contents buf

基本上:电池可以在这样的代码中节省大量的时间和精力。





相关问题
ocamlc, module compilation

I wrote an app in ocaml. It consist of several modules: Util (util.ml) Work1 (work1.ml) -- open Util Work2 (work2.ml) -- open Util, too Main (main.ml) -- open all of them. When i compile its, using ...

How can I simplify this ocaml pattern-matching code?

I m writing a simple little ocaml program that reads an algebraic statement in from a file, parses it into an AST using ocamllex/ocamlyacc, reduces it, and then prints it. The part where I m reducing ...

How can I create a type with multiple parameters in OCaml?

I m trying to create a type that has multiple type parameters. I know how to make a type with one parameter: type a foo = a * int But I need to have two parameters, so that I can parameterize the ...

Hashtable indexed on several fields

I m currently programming an OCaml module defining a type corresponding to a CPU register. The interface of this module is the following : (* * Defines a type which represents a R3000 register. *) ...

Extending an existing type in OCaml

I ve been doing some OCaml programming lately to learn the language and to get more acquainted with functional programming. Recently, I ve started to think that I d like to be able to extend an ...

Ocaml Syntax Error

What s wrong with this code? I can t figure it out: let parent (rules : grammar) (symbol1 : string) (symbol2 : string) : (SymbolSet.t) = try SymbolSet.singleton (getParent [symbol1; symbol2] ...

热门标签