OCaml Python Generator Equivalent

Sécurité Sociale's French identification numbers end with a two-digit check code. I checked that you can detect any possible common transcription error and find some other types of errors (for example, rolling three consecutive digits) that may go unnoticed.

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') 

My Python 2.7 code (working snippet above) makes heavy use of generators. I was wondering how I can adapt them in OCaml. Of course, I can write a function that supports some internal state, but I'm looking for a purely functional solution. Can I learn the lazy library that I am not too familiar with? I do not ask for the code, only instructions.

+6
source share
2 answers

You can simply define the generator as a stream using the language extension:

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

The syntax for cannot be used with this, but there is a set of functions provided by the Stream module to check the state of the stream and iterate over its elements.

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

Or easier:

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

You can also use the special syntax provided by the camlp4 preprocessor:

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

Other features include creating streams from lists, strings, bytes, or even channels. The API documentation briefly describes the various features.

Special syntax allows you to compose them to return elements before or after, but at first it can be somewhat unintuitive:

 let dump = Stream.iter (Printf.printf "next element: %d\n");; dump [< range 10; range 20 >];; 

will create the elements of the first stream, and then take the second stream in the element ranked immediately after the rank of the last received element, so it will appear in this case, as if only the second stream had received an iteration.

To get all the elements, you can build a stream 'a Stream.t and then iterate recursively over each of them:

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

This will lead to the expected result.

I recommend reading an old OCaml book (available online ) for a better introduction to the topic.

+4
source

Core library provides python-style generators, see Sequence .

Here is an example taken from one of my projects:

 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 
+4
source

Source: https://habr.com/ru/post/982068/


All Articles