免责声明:我是ReasonML初学者。
我最近开始使用ReasonML,并且发现与原始JavaScript相比,性能存在很大差异。这是我针对简单难题解决功能的示例(难题取自https://adventofcode.com/2015/day/1)
ReasonML
let head = str =>
switch (str) {
| "" => ""
| _ => String.sub(str, 0, 1)
};
let tail = str =>
switch (str) {
| "" => ""
| _ => String.sub(str, 1, String.length(str) - 1)
};
let rec stringToList = str =>
switch (str) {
| "" => []
| _ => [[head(str)], tail(str) |> stringToList] |> List.concat
};
let rec findFloor = code =>
switch (head(code)) {
| "" => 0
| "(" => 1 + findFloor(tail(code))
| ")" => (-1) + findFloor(tail(code))
| _ => 0
};
let findFloorFold = code => {
let calc = (a, b) =>
switch (b) {
| "" => a
| "(" => a + 1
| ")" => a - 1
| _ => a
};
code |> stringToList |> List.fold_left(calc, 0);
};
JavaScript
const solve = code => {
let level = 0;
for (let i = 0, l = code.length; i < l; i++) {
const el = code[i];
if (el === "(") {
++level;
continue;
}
if (el === ")") {
--level;
continue;
}
}
return level;
};
结果
这是预期的还是我的ReasonML函数实现太慢?
在此先感谢您的澄清/建议。
诚然,第二个解决方案(非rec)由于将字符串转换为数组而速度较慢,但这是因为在ReasonML中,字符串不是由字符列表表示的。
最佳答案
您可以在Reason中编写与在JS中相同的命令式代码,并且实际上使其更快(在我的计算机上提高了30%):
let findFloorImperative = code => {
let level = ref(0);
for (i in 0 to String.length(code) - 1) {
switch (code.[i]) {
| '(' => level := level^ + 1
| ')' => level := level^ - 1
| _ => failwith("invalid code")
}
};
level^
};
这个递归解决方案几乎一样快:
let findFloorRecursiveNoList = code => {
let rec helper = (level, i) =>
if (i < String.length(code)) {
switch (code.[i]) {
| '(' => helper(level + 1, i + 1)
| ')' => helper(level - 1, i + 1)
| _ => failwith("invalid code")
}
} else {
level
};
helper(0, 0)
};
基准测试结果:
Reason, imperative: 19,246,043 ops/sec
Reason, recursive, no list: 18,113,602 ops/sec
JavaScript, imperative: 13,761,780 ops/sec
Reason, recursive, list: 481,426 ops/sec
Reason, folding, list: 239,761 ops/sec
Source: re:bench