OCaml 相当于 Python 生成器

问题描述 投票:0回答:3

法国 Sécurité Sociale 识别号以两位数的校验码结尾。我已经验证了每种可能的常见转录错误都可以被检测到,并发现了一些其他类型的错误(例如,滚动三个连续数字)可能未被检测到。

def check_code(number):
    return 97 - int(number) % 97

def single_digit_generator(number):
    for i in range(len(number)):
        for wrong_digit in "0123456789":
            yield number[:i] + wrong_digit + number[i+1:]

def roll_generator(number):
    for i in range(len(number) - 2):
        yield number[:i] + number[i+2] + number[i] + number[i+1] + number[i+3:]
        yield number[:i] + number[i+1] + number[i+2] + number[i] + number[i+3:]

def find_error(generator, number):
    control = check_code(number)
    for wrong_number in generator(number):
        if number != wrong_number and check_code(wrong_number) == control:
            return (number, wrong_number)

assert find_error(single_digit_generator, "0149517490979") is None
assert find_error(roll_generator, "0149517490979") == ('0149517490979', '0149517499709')

我的 Python 2.7 代码(上面的工作片段)大量使用了生成器。我想知道如何在 OCaml 中调整它们。我当然可以编写一个维护某些内部状态的函数,但我正在寻找一个纯函数式解决方案。我可以学习一下我不太熟悉的图书馆

lazy
吗?我不是要代码,只是要方向。

python ocaml generator
3个回答
8
投票

您可以简单地将生成器定义为stream,使用语言的扩展:

let range n = Stream.from (fun i -> if i < n then Some i else None);;

for
语法结构不能与此一起使用,但
Stream
模块提供了一组函数来检查流的状态并迭代其元素。

try
  let r = range 10 in
  while true do
    Printf.printf "next element: %d\n" @@ Stream.next r
  done
with Stream.Failure -> ();;

或更简单地说:

Stream.iter (Printf.printf "next element: %d\n") @@ range 10;;

您还可以使用 camlp4 预处理器提供的特殊语法:

Stream.iter (Printf.printf "next element: %d\n") [< '11; '3; '19; '52; '42 >];;

其他功能包括从列表、字符串、字节甚至通道创建流。 API 文档 简洁地描述了不同的可能性。

特殊的语法允许您组合它们,以便将元素放回之前或之后,但一开始可能有点不直观:

let dump = Stream.iter (Printf.printf "next element: %d\n");;

dump [< range 10; range 20 >];;

将产生第一个流的元素,然后在最后产生的元素的排名之后的元素排名处拾取第二个流,因此在这种情况下看起来好像只有第二个流被迭代。

要获取所有元素,您可以构造一个

'a Stream.t

 流,然后递归地迭代每个元素:

Stream.iter dump [< '(range 10); '(range 20) >];;

这些将产生预期的输出。

我建议阅读有关 OCaml 的旧书(可以

在线),以获得有关该主题的更好介绍。


6
投票

Core

库提供了Python风格的生成器,请参阅Sequence
模块。

这是一个例子,取自我的一个项目:

open Core_kernel.Std let intersections tab (x : mem) : _ seq = let open Sequence.Generator in let init = return () in let m = fold_intersections tab x ~init ~f:(fun addr x gen -> gen >>= fun () -> yield (addr,x)) in run m
    

0
投票
自从

OCaml 4.07

(于 2018 年发布,因此在提交其他答案时不可用)以来,您可以使用标准 Seq
 模块:

let range (start : int) (end_ : int) : int Seq.t = let step = if end_ > start then +1 else -1 in Seq.unfold ( fun x -> if x = end_ then None else Some (x, x + step) ) start ;; let numbers = range 4 (-7);; Seq.iter (fun x -> Printf.printf "%d " x) numbers;;
输出:

4 3 2 1 0 -1 -2 -3 -4 -5 -6



可以通过

Seq.map

, 
Seq.filter
, ...
转换序列

此外,OCaml 4.14 添加了

许多函数来组合序列,例如 Seq.zip

Seq.interleave
Seq.product
...
演示:

let numbers_mul_by_3 = Seq.map (( * ) 3) (range 0 5);; let chars = List.to_seq ['a'; 'b'; 'c'];; let cartesian_product = List.sort compare (List.of_seq @@ Seq.product numbers_mul_by_3 chars) ;; List.iter (function | (num, ch) -> Printf.printf "(%d, %c) " num ch) cartesian_product ;;
输出:

(0, a) (0, b) (0, c) (3, a) (3, b) (3, c) (6, a) (6, b) (6, c) (9, a) (9, b) (9, c) (12, a) (12, b) (12, c)


    

© www.soinside.com 2019 - 2024. All rights reserved.