Проверка диапазона и длины в Аде (режим SPARK)

в последние недели я пытался выучить язык ADA, для этого я сделал упражнение, чтобы перевернуть строку с помощью рекурсии, однако, когда я скомпилирую ее с помощью GNATProve, это дает мне несколько ошибок, которые я не смог решить, это было бы Если бы вы могли помочь мне решить их, используя предварительные условия и постусловия, вы бы мне очень помогли.

Мой код:

function String_Reverse(Str:String) return String with
        Pre => Str'Length > 0 ,
        Post => String_Reverse'Result'Length <= Str'Length;
        function String_Reverse (Str : String) return String is
            Result : String (Str'Range);
        begin
            if Str'Length = 1 then
                Result := Str;
            else
                Result :=
                   String_Reverse (Str (Str'First + 1 .. Str'Last)) &
                   Str (Str'First);
         end if;
            return Result;
        end String_Reverse;

Ошибки:

dth113.adb:18:69: low: range check might fail
   18>|                   String_Reverse (Str (Str'First + 1 .. Str'Last)) &
   19 |                   Str (Str'First);
  reason for check: result of concatenation must fit in the target type of the assignment
  possible fix: precondition of subprogram at line 8 should mention Str
    8 |      function String_Reverse(Str:String) return String with
      |      ^ here
dth113.adb:18:69: medium: length check might fail
   18>|                   String_Reverse (Str (Str'First + 1 .. Str'Last)) &
   19 |                   Str (Str'First);
  reason for check: array must be of the appropriate length
  possible fix: precondition of subprogram at line 8 should mention Str
    8 |      function String_Reverse(Str:String) return String with
      |      ^ here

Я пробовал использовать Preconditons и Postconditions о длине ввода Str.

Разве длина результата не должна быть такой же, как длина ввода?

Simon Wright 31.10.2022 22:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
102
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ваша программа продолжает добавлять большую часть строки к вашему результату. Это делает строку намного больше, чем исходный строковый параметр. Вы не можете исправить это с помощью улучшенных предварительных условий или условий публикации.

Посмотрите разницу между вашим решением и функцией Reverse_String, показанной ниже. В приведенном ниже решении размер возвращаемой строки не может быть размером строки функции в параметре, потому что она строит обратную строку с каждой рекурсией, и размер строки не может быть определен до завершения всех рекурсий.

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   function Reverse_String(S : in String; Idx : Positive) return String is
   begin
      if Idx < S'Last then
         return Reverse_String(S, Idx + 1) & S(Idx);
      else
         return S(Idx) & "";
      end if;
   end Reverse_String;

   S1 : String := "Hello World";
   s2 : String := "1234567890";
   N1 : Integer := 12345;
   N3 : Integer;
begin
   Put_Line(S1 & " : " & Reverse_String(S1, 1));
   Put_Line(S2 & " : " & Reverse_String(S2, 1));
   N3 := Integer'Value(Reverse_String(N1'Image, 1));
   Put_Line(N1'Image & " : " & N3'Image);
end Main;

Эта версия Reverse_String продолжает увеличивать индекс входной строки и конкатенировать этот символ в конце всех символов, которые еще предстоит реверсировать. Предложение else в Reverse_String добавляет пустую строку к последнему символу, чтобы компилятор рассматривал ее как строку, а не как символ, потому что S(Idx) — это одиночный символ, а не строка, а объединение S(Idx) с пустая строка приводит к строке.

Вот еще одна реализация

function String_Reverse (Str : String) return String is
    Result : String (Str'Range) := Str;
begin       
    for I in reverse Str'Range loop
        Result(Str'Last - I + Result'First) := Str(I);
    end loop;
    return Result;
end String_Reverse;   

Не использует рекурсию.

Simon Wright 01.11.2022 09:34
Ответ принят как подходящий

У Gnatprove, похоже, есть некоторые трудности с конкатенацией массивов.

Ниже это доказывается с использованием подтипов, а не пред- и постусловий. Доказать, что результат на самом деле является обратным входной строке, может быть сложнее!

Спецификация:

pragma SPARK_Mode;
function String_Reverse (Str : String) return String with
  Pre => Str'Length > 0,
  Post => String_Reverse'Result'Length = Str'Length;

Тело:

pragma SPARK_Mode;
function String_Reverse (Str : String) return String is
   subtype Result_String is String (Str'Range);
   Result : Result_String;
begin
   if Str'Length = 1 then
      Result := Str;
   else
      declare
         subtype Part_String is String (1 .. Str'Length - 1);
         Reversed_Part : constant Part_String
           := String_Reverse (Str (Str'First + 1 .. Str'Last));
      begin
         Result := Reversed_Part & Str (Str'First);
      end;
   end if;
   return Result;
end String_Reverse;

С небольшим изменением вы также можете обрабатывать строки нулевой длины.

Большое спасибо Саймон! Я вижу, что предстоит пройти долгий путь, чтобы изучить этот язык. Ваш ответ мне очень помог

Cristian 01.11.2022 17:48

Во многих случаях SPARK больше подходит для функций-выражений, чем для обычных функций. Я не знаю, будет ли это иметь значение, но вы можете попробовать переписать тело своей функции как

function String_Reverse (Str : String) return String is
   (if Str'Length = 1 then
       Str
    else
       String_Reverse (Str (Str'First + 1 .. Str'Last) ) & Str (Str'First) );

(Надеюсь, я уравновесил скобки.)

Обычно разрешаются пустые строки, и функция становится чем-то вроде

function Reversed (S : in String) return String with
   Post => Reversed'Result'Length = S'Length;

function Reversed (S : in String) return String is
   (if S'Length = 0 then ""
    else Reversed (S (S'first + 1 .. S'Last) & S (S'First) );

(Подсказка: «Ада» — это женское имя, а не аббревиатура. GNATProve — это инструмент статического анализа, а не компилятор.)

Другие вопросы по теме