2016-10-05 39 views
1

我一直在将字符列表转换为int列表时遇到问题。我的目标是基本上取一个数字,如325,并返回一个[3,2,5]的列表。到目前为止,我所做的是取出数字,然后将其转换为字符串,然后将其分解为char数组。然后我想将每个字符转换为相应的int。当我将char列表映射到fn c => Char.ord(c)时,char列表变成了一个?.int列表,这阻止了我对它进行操作(+, - )。我是ML新手,对其类型系统没有很强的把握,但对我来说似乎很奇怪。将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列表类型。原始问题可以发现为Project Euler problem 20

+1

我添加了一些通用代码反馈给我的答案。 –

回答

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

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]; 
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 

或者,如果你认识到的语法糖op::是完全一样fn (d, res) => d::res)和也对参数进行neta 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