Как сжать последовательность половин целых чисел до строковых выражений с помощью операторов двоеточия? (Как преобразовать список в строку) - PullRequest
4 голосов
/ 03 октября 2019

Есть ли в Matlab встроенная функция, которая конденсирует последовательность половин целых чисел в выражения с операторами двоеточия?

Например, [1:4,5:.5:7] дает

1, 2, 3, 4, 5, 5.5, 6, 6.5, 7

Учитывая двойной массивнапример, [1, 2, 3, 4, 5, 5.5, 6, 6.5, 7], есть ли удобный способ преобразовать его обратно в [1:4,5:.5:7] - или, в равной степени, в [1:5,5.5:.5:7] - как строку?

Ответы [ 2 ]

3 голосов
/ 04 октября 2019

Вот решение, которое

  • Адекватно обрабатывает числа, которые не образуют диапазон . Например, [1 3 5 9] будет выводиться как '[1:2:5 9]'. Точно так же [1 3 5 9 11] даст '[1:2:5 9 11]'.
  • Пропуски, указывающие step 1. Например, [9 3 4 5] даст [9 3:5].
  • Пропускает ненужные скобки . Например, [8 6 4 2] даст '8:-2:2', а 5 даст '5'.
  • Позволяет пустой ввод . Так что [] даст '[]'.

x = [2 4.5 7 9.5 9 8 7 6 5 15 7.5 7 6.5 6 9 11]; % example input
sep = ' '; % define separator; it could also be comma
str = ''; % initiallize output
k = 1; % first number not processed yet
while k<=numel(x)
    m = find(diff([diff(x(k:end)) inf]), 1) + 1; % may be empty
    if m>2 % if non-empty and at least 2: range found (at least 3 numbers)
        ini = x(k);
        ste = x(k+1)-x(k);
        fin = x(k+m-1);
        if ste~=1
            str = [str num2str(ini) ':' num2str(ste) ':' num2str(fin)];
        else
            str = [str num2str(ini) ':' num2str(fin)];
        end
        k = k+m; % m numbers have been processed
    else % no range: include just one number
        str = [str num2str(x(k))];
        k = k+1; % 1 number has been processed
    end
    str = [str sep]; % add separator
end
str = strip(str,sep); % this removes trailing space/comma, if it exists. For pre-2016b, use `strtrim`
if any(str==sep) || isempty(str)
    str = ['[' str ']']; % brackets are required
end

Примеры / тесты:

  • [2 4.5 7 9.5 9 8 7 6 5 15 7.5 7 6.5 6 9 11] дает '[2:2.5:9.5 9:-1:5 15 7.5:-0.5:6 9 11]'
  • [1.5 16 -0.5 -7 -9 -11] т '[1.5 16 -0.5 -7:-2:-11]'
  • [4 2 0 -2 5 12 19] т '[4:-2:-2 5:7:19]'
  • [-2 0 2.5 5.5] т '[-2 0 2.5 5.5]'
  • [2 3 4 10 7 8] т'[2:4 10 7 8]'
  • [6 4.5 3] т '6:-1.5:3'
  • [3 4 5 6] т '3:6'
  • 42 т '42'
  • [] дает '[]'

Код состоит из цикла, который ищет диапазон максимальной длины, начиная с текущей позиции, а затем двигается вперед. самая хитрая часть - это строка

m = find(diff([diff(x(k:end)) inf]), 1) + 1; % may be empty

Это пытается найти максимальную длину m чисел, образующих диапазон,начиная с текущей позиции k. diff(x(k:end)) вычисляет последовательные различия, а внешний diff обнаруживает изменения в этих различиях. Первое такое изменение, вычисленное с find(..., 1), указывает на первое число, которое не принадлежит диапазону. Существует пять случаев, второй из которых объясняет, почему необходим inf:

  • Правильный диапазон из 3 или более чисел, за которым следует хотя бы число, не входящее в этот диапазон . Например, если x(k:end) равно [3 5 7 15], то diff(x(k:end)) равно [2 2 8], diff([diff(x(k:end)) inf]) равно [0 6 inf], find(..., 1) дает 2, а m равно 3.
  • Правильный диапазон из 3 или более чисел, который заканчивается массивом . Например, если x(k:end) равно [3 5 7], то diff(x(k:end)) равно [2 2], diff([diff(x(k:end)) inf]) равно [0 inf], find(..., 1) дает 2, а m равно 3. Вот почему inf необходим;без него результатом будет m=[], что будет неправильно интерпретировано как "отсутствие диапазона" ветвью if.
  • Нет правильного диапазона;осталось более 2 номеров . Например, если x(k:end) равно [3 6 7], то diff(x(k:end)) равно [3 1], diff([diff(x(k:end)) inf]) равно [-2 inf], find(..., 1) дает 1, а m равно 2. Это означает, что - это диапазон двух чисел;но это не правильный диапазон, поэтому ветвь if будет игнорировать его, и выполнение продолжится с частью else.
  • Нет правильного диапазона;осталось только 2 номера . Например, если x(k:end) равно [3 6], то diff(x(k:end)) равно 3, diff([diff(x(k:end)) inf]) равно [inf], find(..., 1) дает 1, а m равно 2. Опять же, есть диапазон из двух чисел, но это не правильный диапазон. Обратите внимание, что без включенного inf у нас было бы m=[] вместо «правильного» * ​​1161 *, но это также было бы допустимо для запуска части else (в которой фактическое значение m не используется).
  • Нет правильного диапазона;осталось только 1 число, то есть текущее число оканчивает массив . Например, if x(k:end) это просто 3, у нас diff(x(k:end)) равно [], diff([diff(x(k:end)) inf]) равно [], find(..., 1) дает [], а m равно []. Хотя m "должно" быть 1, [] так же верно для запуска части else.

Обратите внимание, что, поскольку вход содержит целые числа или половину целых, есть нет проблем с плавающей точкой , так как эти числа представлены с точностью до ± 2^52.

0 голосов
/ 04 октября 2019

вот простой зацикленный ответ, учитывая x ваш вектор, я выбрал 1e-10 в качестве порога разности для точности с плавающей запятой ...

x=[1, 2, 3, 4, 5, 5.5, 6, 6.5, 7];
dx=diff(x);
a=num2str(x(1)); % first step, start the range
for n=2:numel(dx)
    if abs(dx(n)-dx(n-1))>1e-10
        if dx(n-1)~=1
        a=[a ':' num2str(dx(n-1)) ':' num2str(x(n)), ' ', num2str(x(n+1)) ];
        else
       a=[a ':' num2str(x(n)), ' ', num2str(x(n+1)) ];
        end     
    end
end
a=[a ':' num2str(dx(end)) ':' num2str(x(end))]; % last step, close the range


> a =

    '1:5 5.5:0.5:7'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...