- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在 OCaml 中,可以通过引发异常提前退出命令式循环。
虽然在 OCaml 中使用命令式循环本身并不是惯用的,但我想知道用早期退出来模拟命令式循环的最惯用方法是什么(如果可能的话,考虑到性能等方面)。
例如,an old OCaml FAQ提到异常 Exit
:
Exit
: used to jump out of loops or functions.
The
Exit
exception is not raised by any library function. It is provided for use in your programs.
let exit = Exit
避免在循环内分配的异常。还需要吗?
raise (Leave 42)
.是否有惯用的异常(exception)或命名约定来执行此操作?在这种情况下我应该使用引用(例如
let res = ref -1 in ... <loop body> ... res := 42; raise Exit
)吗?
Exit
in nested loops 防止了一些想要退出多个循环的情况,例如
break <label>
在 java 。这将需要定义具有不同名称的异常,或者至少使用一个整数来指示应该退出多少范围(例如
Leave 2
来指示应该退出 2 个级别)。同样,这里是否有一种惯用的方法/异常命名?
最佳答案
正如最初在评论中发布的那样,在 OCaml 中提前退出的惯用方法是使用延续。在您希望提前返回的地方,您创建一个延续,并将其传递给可能提前返回的代码。这比循环的标签更通用,因为您几乎可以从任何可以访问延续的东西中退出。
此外,正如评论中所发布的,请注意 raise_notrace
的用法。对于您不希望运行时生成其跟踪的异常。
“天真”的第一次尝试:
module Continuation :
sig
(* This is the flaw with this approach: there is no good choice for
the result type. *)
type 'a cont = 'a -> unit
(* with_early_exit f passes a function "k" to f. If f calls k,
execution resumes as if with_early_exit completed
immediately. *)
val with_early_exit : ('a cont -> 'a) -> 'a
end =
struct
type 'a cont = 'a -> unit
(* Early return is implemented by throwing an exception. The ref
cell is used to store the value with which the continuation is
called - this is a way to avoid having to generate an exception
type that can store 'a for each 'a this module is used with. The
integer is supposed to be a unique identifier for distinguishing
returns to different nested contexts. *)
type 'a context = 'a option ref * int64
exception Unwind of int64
let make_cont ((cell, id) : 'a context) =
fun result -> cell := Some result; raise_notrace (Unwind id)
let generate_id =
let last_id = ref 0L in
fun () -> last_id := Int64.add !last_id 1L; !last_id
let with_early_exit f =
let id = generate_id () in
let cell = ref None in
let cont : 'a cont = make_cont (cell, id) in
try
f cont
with Unwind i when i = id ->
match !cell with
| Some result -> result
(* This should never happen... *)
| None -> failwith "with_early_exit"
end
let _ =
let nested_function i k = k 15; i in
Continuation.with_early_exit (nested_function 42)
|> string_of_int
|> print_endline
k
你想要的深度。也可以定义函数
f
立即传递给
with_early_exit
,产生类似于在循环上添加标签的效果。我经常使用这个。
'a cont
的结果类型,我任意设置为
unit
.实际上,
'a cont
类型的函数永远不会返回,所以我们希望它表现得像
raise
– 可用于任何类型的预期。但是,这不会立即起作用。如果您执行
type ('a, 'b) cont = 'a -> 'b
之类的操作,并将其传递给您的嵌套函数,类型检查器将推断
'b
的类型在一种情况下,然后强制您仅在具有相同类型的上下文中调用延续,即您将无法执行类似的操作
(if ... then 3 else k 15)
...
(if ... then "s" else k 16)
'b
成为
int
,但第二个需要
'b
成为
string
.
raise
的函数。提前返回,即
(if ... then 3 else throw k 15)
...
(if ... then "s" else throw k 16)
make_cont
上面(我将它重命名为
throw
),然后传递裸上下文:
module BetterContinuation :
sig
type 'a context
val throw : 'a context -> 'a -> _
val with_early_exit : ('a context -> 'a) -> 'a
end =
struct
type 'a context = 'a option ref * int64
exception Unwind of int64
let throw ((cell, id) : 'a context) =
fun result -> cell := Some result; raise_notrace (Unwind id)
let generate_id = (* Same *)
let with_early_exit f =
let id = generate_id () in
let cell = ref None in
let context = (cell, id) in
try
f context
with Unwind i when i = id ->
match !cell with
| Some result -> result
| None -> failwith "with_early_exit"
end
let _ =
let nested_function i k = ignore (BetterContinuation.throw k 15); i in
BetterContinuation.with_early_exit (nested_function 42)
|> string_of_int
|> print_endline
throw k v
可以在需要不同类型的上下文中使用。
with_early_exit
签名大致如下:
val with_early_exit : ('a context -> 'b) -> ('a -> 'b) -> 'b
'a
的错误的处理程序这可能会导致。与变体和多态变体一起,这为异常处理提供了更明确的类型。它对多态变体特别强大,因为编译器可以推断出错误变体集。
关于loops - OCaml 中退出循环的惯用异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30936958/
我正在尝试以更命令的方式表达一组链式调用。例如,图像我们有一个函数,它接受一个列表和一个元素,并将该元素附加到列表的末尾: let insert l e = l @ [e] 我想一次插入几个元素。
是否有一个完整的列表(或详尽的规则)可以给 OCaml 中的自定义中缀运算符提供可能的名称? 最佳答案 如 OCaml manual 中所述, 中缀运算符必须匹配正则表达式 [-=<>@^|&+*/$
我想开始使用 OCaml 编程。由于我是 Windows 用户,我知道最好使用 Netbeans 的 OCaml 插件。 我已经从以下链接下载了上述插件:http://ocamlplugin.loki
能够做到这一点以获得最大速度不是很重要吗? 编辑: 例如,Clojure 有 map ,它使用多个核心。 Harrop 博士写道(2011 年 1 月 9 日): 语言中添加的新功能,例如一流 OCa
我在 x86 机器上用字节码编译 OCaml 程序,然后将字节码传输到 ppc64 机器。假设 ppc64 机器有 ocamlrun(为 ppc64 编译),我能在 ppc64 架构上执行我的程序吗?
我创建了一个新模块,它只是名称很长的模块的较短别名: module M = ModuleWithLongName 我处于最终可执行文件的大小很重要的情况。上面的构造是由编译器合理处理的吗(即 M 实际
我在使用 ocaml 时遇到了麻烦。 我想创建一个函数,每次调用它时都会增加我的计数器,并将我的 vargen 字符串与计数器编号连接起来,然后返回这个新字符串。 我没有成功的做法是: let (co
我正在学习 OCaml,我对变量的不变性有点困惑。根据我正在阅读的书,变量是不可变的。到目前为止一切顺利,但为什么我可以这样做: let foo = 42 let foo = 4242 我错过了什么?
非尾递归组合函数可以这样写: let rec combinations l k = if k List.length l then [] else if k = 1 then List.ma
我有一段包含camlp4引用的代码。 let f_name = "my_func" > 运行此程序后 camlp4of ,它产生这个: Ast.StExp (_loc, (Ast.ExAp
如何在 OCaml 中模拟这个 Python 习语? if __name__=="__main__": main() 见 RosettaCode其他编程语言中的示例。 最佳答案 Ocaml 中没
我开始学习 Ocaml,使用 hickey book ,我被困在练习 3.4,第 9 部分 让 x x = x + 1 在 x 2 运算结果为3 ,但我不明白为什么? 最佳答案 当你写 let x x
Rust具有线性系统。有什么(好的)方法可以在 OCaml 中模拟这个吗?例如,当使用 ocaml-lua 时,我想确保仅当 Lua 处于特定状态(堆栈顶部的表等)时才调用某些函数。 编辑 :这是最近
在 OCaml?我知道非常酷的Bitstring图书馆,但是 虽然这将是在某些协议(protocol)中解析二进制数据的好方法, 它不支持异或或移位等按位运算。 我相信该库使用的底层数据结构是 只是
我们可以像这样构造一个无限列表: let rec endless = 1::endless 我认为它会吃掉所有的内存,但是当我在 utop 中尝试时,好像不是这样。 utop显示列表已构建: val
是否可以通过合并列表的元素而不是创建列表的列表来创建列表? 例子: List.combine ["A";"B"] ["C";"D"];; 我得到: [("A", "C"); ("B", "D")] 有
我想在 OCaml 中创建一个查找表。该表将有 7000 多个条目,在查找时(通过 int)返回一个字符串。用于此任务的适当数据结构是什么?表是否应该从基本代码中外部化,如果是这样,如何“包括”查找表
我正在评估 Ocaml 顶层中的一段非常简单的代码: let p5 () = print_int 5;; p5 ();; print_string "*************************
记录和元组之间是否有任何区别而不仅仅是句法差异? 有性能差异吗? 元组和记录的实现是否相同? 您是否有可以使用元组完成但不能使用记录完成的事情的示例(和 反之)? 最佳答案 模数语法它们几乎相同。主要
OCaml 中的 ` 运算符有什么作用? let int_of_meth = function | `GET -> 0 | `POST -> 1 | `PUT ->
我是一名优秀的程序员,十分优秀!