下面的函数tally
非常简单:它将一个字符串s
作为参数,将其拆分为非字母数字字符,并以不区分大小写的方式计算得到的“单词”的数字。
open Core.Std
let tally s =
let get m k =
match Map.find m k with
| None -> 0
| Some n -> n
in
let upd m k = Map.add m ~key:k ~data:(1 + get m k) in
let re = Str.regexp "[^a-zA-Z0-9]+" in
let ws = List.map (Str.split re s) ~f:String.lowercase in
List.fold_left ws ~init:String.Map.empty ~f:upd
我认为这个函数比杂乱应该更难阅读。我希望我能写一些更接近这一点的东西(我已经沉迷于一些“幻想语法”):
(* NOT VALID SYNTAX -- DO NOT COPY !!! *)
open Core.Std
let tally s =
let get m k =
match find m k with
| None -> 0
| Some n -> n ,
upd m k = add m k (1 + get m k) ,
re = regexp "[^a-zA-Z0-9]+" ,
ws = map (split re s) lowercase
in fold_left ws empty upd
我上面做的改变主要分为三组:
let ... in
,合并所有绑定(进入,
- 分开的序列;这个,AFAIK,无效OCaml); ~foo:
类型噪音; Str.
,List.
等我可以使用有效的OCaml语法实现类似的效果吗?
答案 0 :(得分:3)
可读性难以实现,它在很大程度上取决于读者的能力和对代码的熟悉程度。我将仅关注语法转换,但如果您正在寻找的话,您可以以更紧凑的形式重构代码。
要删除模块限定符,只需事先打开它们:
open Str
open Map
open List
您必须按顺序打开它们,以确保您在那里使用的List
值仍然可以访问,而不是Map
覆盖范围。
对于带标签的参数,如果对于每个函数调用,您可以省略标签,并在函数签名顺序中提供函数的所有参数。
要减少let...in
构造的数量,您有以下几种选择:
使用一组rec定义:
let tally s =
let rec get m k =
match find m k with
| None -> 0
| Some n -> n
and upd m k = add m k (1 + get m k)
and re = regexp "[^a-zA-Z0-9]+"
and ws = map lowercase (split re s)
in fold_left ws empty upd
一次制作多个定义:
let tally s =
let get, upd, ws =
let re = regexp "[^a-zA-Z0-9]+" in
fun m k ->
match find m k with
| None -> 0
| Some n -> n,
fun g m k -> add m k (1 + g m k),
map lowercase (split re s)
in fold_left ws empty (upd get)
使用模块对定义进行分组:
let tally s =
let module M = struct
let get m k =
match find m k with
| None -> 0
| Some n -> n
let upd m k = add m k (1 + get m k)
let re = regexp "[^a-zA-Z0-9]+"
let ws = map lowercase (split re s)
end in fold_left ws empty M.upd
后者让人联想到Sml语法,或许更适合编译器进行适当的优化,但它只能删除in
个关键字。
请注意,由于我不熟悉Core Api,我可能写错了代码。
答案 1 :(得分:3)
如果您对相同的值有一系列计算,那么在OCaml中有一个|>
运算符,它从左侧获取一个值,并应用于右侧的函数。这可以帮助你摆脱"摆脱" let
和in
。关于标记的参数,然后您可以通过回退到一个vanilla标准库来摆脱它们,并使您的代码更小,但可读性更低。无论如何,有一小块带有标记参数的糖,你总是可以写f ~key ~data
而不是f ~key:key ~data:data
。最后,模块名称可以通过本地开放语法(let open List in ...
)删除,也可以通过本地将其删除为较小的名称(let module L = List in
)。
无论如何,我想向你展示一个包含较少杂乱的代码,我认为:
open Core.Std
open Re2.Std
open Re2.Infix
module Words = String.Map
let tally s =
Re2.split ~/"\\PL" s |>
List.map ~f:(fun s -> String.uppercase s, ()) |>
Words.of_alist_multi |>
Words.map ~f:List.length