Вычитание в Ocaml, я не могу полностью устранить эту ошибку

#functional-programming #ocaml

#функциональное программирование #ocaml

Вопрос:

 let rec sub' list1 list2 carry = match (list1, list2, carry) with
    | list1, [], 0       -> list1
    | [], list2, 0       -> list2
    | list1, [], carry   -> sub' list1 [carry] 0
    | [], list2, carry   -> sub' [carry] list2 0
    | car1::cdr1, car2::cdr2, carry ->
      let minus = car1 - car2   carry



       in  minus mod radix :: sub' cdr1 cdr2 (minus / radix)
  

значение radix равно 10

Итак, это код, который я должен вычесть два числа (list1 и list2). Это основано на данном коде, который я модифицировал, поэтому я не совсем уверен, как это работает. Однако это работает для большинства вещей. 5 — 4 = 1 10 — 20 = -10 и т.д.

Но в нем есть эта странная ошибка для больших чисел, 100 — 50 = «1-50», что довольно близко, в нем есть «50», которые мы хотим, но с некоторым дополнительным мусором.

Я пытался разобраться в этом некоторое время, и я довольно сильно застрял.

Редактировать:

 let length    = List.length
let car       = List.hd
let cdr       = List.tl
let map       = List.map
let reverse   = List.rev
let strcat    = String.concat
let strlen    = String.length
let strsub    = String.sub
let zero      = Bigint (Pos, [])



let rec zeros' rev = 
if (car rev) = 0 then
  lead_zeros' (cdr rev)
else (reverse rev)

let zeros list = match list with
| [0] -> list
| [] -> []
| list -> let reversed = reverse list
          in lead_zeros' reversed
  

Комментарии:

1. Как сделать «123» «45» на бумаге? Потому что этот код работает не так. Вычитание и сложение — это одна и та же фундаментальная операция.

2. Я не уверен, к чему вы клоните. Мое сложение работает нормально. 123 45 — это 5 3, без переноса, 4 6, без переноса, 1 0. Я не уверен, как вставлять новые строки в комментарии, но я помню, как добавлять на бумаге.

3. можете ли вы привести нам пример того, как вы применили эту sub' функцию для получения результата 5-4 , 10-20 и 100-50

4. @JaneDoe car :: cdr — как сопоставить car / cdr с цифрами и последовательностью итераций? Если последовательность числовых цифр не изменена на противоположную (перед вызовом things function), сначала не выполняется ‘5 3’.

5. @user2864740 Готово, смотрите редактирование, и если я вас правильно понимаю, то да, последовательность уже поменялась на противоположную.

Ответ №1:

Я не анализировал ваш код полностью, но я полагаю, что проблема в том, что деление округляется до нуля, в то время как вам нужно было бы округлить его в меньшую сторону. Например, (-3) / 5 = 0 и (-3) mod 5 = -3 , в то время как вам понадобились бы (-3) / 5 = -1 и (-3) mod 5 = 2 .

Кажется, что инвариантом является minus >= -radix , поэтому простым исправлением было бы добавить radix к minus и соответствующим образом настроить все остальное:

 let minus = car1 - car2   carry   radix
  in  minus mod radix :: sub' cdr1 cdr2 (minus / radix - 1)
  

Это переводит деление по модулю в неотрицательный диапазон, где оно работает так, как ожидалось.

Комментарии:

1. Вы имеете в виду что-то вроде этого: в if diff < 0 тогда diff radix :: sub’ cdr1 cdr2 (0 — radix) иначе diff :: sub’ cdr1 cdr2 0?