我一直遇到将字符列表转换为int列表的问题。我的目标是基本上取一个像325这样的数字并让它返回[3,2,5]的列表。到目前为止我所做的是获取数字,然后将其转换为字符串,然后将其分解为char数组。然后我想将每个char转换为相应的int。当我将我的char列表映射到fn c => Char.ord(c)char列表变成了一个?.int列表,它阻止我对它进行操作(+, - )。我是ML的新手并没有对它的类型系统有很强的把握,但对我来说这似乎很奇怪。
以下是代码:
open IntInf;
fun fact_helper (0, r : int) = r
| fact_helper (n : int, r : int) = fact_helper (n-1, n*r);
fun factorial n:int = fact_helper (n, 1);
fun num_to_digits n =
let val digits_as_chars = explode (IntInf.toString n);
in map (fn c => (Char.ord c)) digits_as_chars
end;
理想情况下,我希望能够在我的映射函数中执行fn c => (Char.ord c) - 48
以获得真正的数字值。我以前做过类似的事情然后它工作但现在不行,我不确定为什么我得到?。int list 类型。原始问题可以在Project Euler problem 20找到。
答案 0 :(得分:4)
问题是您执行了open IntInf
,因此类型int
和运营商+
以及朋友现在引用IntInf
模块。普通int
类型被IntInf.int
遮蔽,因此打印为?.int
(SML / NJ使用伪语法?.x
来引用来自不可访问范围的名称)。 Char.ord
返回普通的int
类型。
因此,您的代码没有任何问题,但open
可能会产生令人困惑的效果。通常应该避免在顶层范围内使用open
。
如果您真的希望num_to_digits
函数使用无限整数进行计算,那么您必须打电话给IntInf.fromInt
(或只是fromInt
,因为IntInf
是在Char.ord c
附近开了。
答案 1 :(得分:2)
首先对您的代码提出一些反馈意见:
(fn c => (Char.ord c))
中的内括号不是必需的。Char.ord
相当于fn c => Char.ord c
,您可以写map ord chars
。 fun factorial n:int = ...
并不代表您认为的含义。此处的:int
部分是指factorial
的返回类型,它与n
类型的顺序相同。你可能想说的是,但由于类型推断而没有必要说:
fun factorial (n : int) : int = ...
通常,类型注释是不必要的。代码很简单:
fun fact_helper (0, r) = r
| fact_helper (n, r) = fact_helper (n-1, n*r);
fun factorial n = fact_helper (n, 1);
接下来,在Andreas' es和galfisher的建议的基础上,你可能想要使用IntInf和数字运算符。此外,在IntInf中有一个非常简洁的函数divMod
,它为你提供了除法和余数:
open IntInf
fun digits n =
let fun aux n res =
case divMod (n, 10) of
(0, d) => d::res
| (n', d) => aux n' (d::res)
in aux n [] end
但是什么时候你真的需要一个数字列表?很可能你会想要在该列表上进行递归并构建其他内容,例如:一个数字之和,或其他什么。递归模式 - 连续访问列表中的每个元素 - 也可以直接应用于数字并推广到折叠中:
(* f is the operator that we fold with
* e is the initial accumulated value (temporary result)
* n is the number on which we fold across
*)
fun folddigits f e n =
case divMod (n, 10) of
(0, d) => f (d, e)
| (n', d) => folddigits f (f (d, e)) n'
通过使用::
运算符折叠,您可以轻松地将数字转换为列表:
fun digits n = folddigits (fn (d, res) => d::res) [] n
或者,如果您意识到语法sugar op::
与fn (d, res) => d::res)
完全相同,并且还在参数n
上执行eta conversion:
val digits = folddigits op:: []
或数字之和(递归应用,直到剩下一位数字):
val sum_of_digits = folddigits
(fn (d, res) => let val res = d + res in
if res < 10 then res else 1 + (res mod 10)
end) 0
答案 2 :(得分:0)
如果要将数字转换为其数字列表,可以使用此递归公式(@
是列表附加运算符)
list(digits(num)) = list(digits(num/10)) @ list(n % 10)
这是SMLNJ中的解决方案:
fun num_to_array 0 = []
| num_to_array n = num_to_array(n div 10) @ [n mod 10];