2016-10-05 9 views
1

У меня возникла проблема с преобразованием списка символов в список int. Моя цель состоит в том, чтобы в основном взять число, такое как 325, и вернуть ему список из [3,2,5]. То, что я сделал до сих пор, - это взять число, а затем преобразовать его в строку, которая затем будет взорвана в массив символов. Затем я хочу преобразовать каждый символ в соответствующий int. Когда я отображаю свой список символов в fn c => Char.ord (c), список char становится списком? .int, что мешает мне выполнять операции (+, -) на нем. Я новичок в ML и не очень понимаю его систему типов, но мне кажется странным.Наложение списка ML при преобразовании списка символов в список int

Вот код:

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.

+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) и также выполняет eta conversion от параметра n:

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