Интерпретировать свободные диапазоны

13

Интерпретировать свободные диапазоны

ListSharp - это интерпретируемый язык программирования, который имеет много функций, одна из которых - это создатель диапазона на основе 1 индекса, который работает следующим образом:

Вы определяете диапазон как (INT) TO (INT)или только (INT)где оба или одно целое может перейти от минимального до максимального значения int32

Затем вы можете использовать эти диапазоны для извлечения элементов массива, не боясь перешагнуть его границы.


следовательно:

1 TO 5 генерирует: {1,2,3,4,5}

3 генерирует: {3}

Диапазоны можно сложить с помощью ANDоператора

1 TO 5 AND 3 TO 6 генерирует: {1,2,3,4,5,3,4,5,6}

помните, что это работает и с отрицательными числами

3 TO -3 генерирует: {3,2,1,0,-1,-2,-3}


Проблема заключается в следующем:

вход

Массив символов и ранее определенное предложение диапазона в виде строки

Выход

Элементы в позициях диапазона на основе индекса 1 (несуществующие / отрицательные индексы переводятся в пустой символ)


Как победить

В качестве задачи по вы должны создать программу с самым коротким числом байтов, чтобы выиграть


Было отмечено, что пустых символов не существует, поэтому вы должны их игнорировать (я только показал их здесь, чтобы было легче понять, но это запутало людей)

Тестовые случаи:

input array is:
{'H','e','l','l','o',' ','W','o','r','l','d'}

range clause:
"1 TO 3" => "Hel"
"5" => "o"
"-10 TO 10" => "Hello Worl"
"0 AND 2 AND 4" => "el"
"8 TO 3" => "oW oll"
"-300 AND 300" => ""
"1 TO 3 AND 3 TO 1" => "HelleH"
"-20 TO 0 AND 1 AND 4" => "Hl"
downrep_nation
источник
3
Разрешено ли использовать 0-index вместо 1-index в качестве входной строки? Таким образом, предложение диапазона становится "0 TO 2"=> {'H', 'e', 'l'}?
Кевин Круйссен
Таблица ASCII не имеет пустых символов (исключая непечатные). Что не так с использованием пробела?
Adrianmp
массив символов - это, в основном, строка, поэтому пустые символы просто не будут напечатаны или возвращены
downrep_nation
1
Кроме того, будет ли 3 TO 3когда-нибудь вход, и каков ожидаемый результат?
Джордан,
1
Вам нужно несколько тестов для определения ANDкратных диапазонов. Кроме того, Вы не ответили, можем ли мы использовать индексацию с нуля, которая является стандартной в большинстве языков.
mbomb007 30.09.16

Ответы:

5

Python 2 - 239 211 210 байт

Спасибо @ mbomb007 и @Cyoce за дальнейшую игру в гольф!

def r(s):
 t=[]
 for x in s.split("AND"):
  if"T"in x:a=map(int,x.split("TO"));b=2*(a[0]<a[1])-1;t+=range(a[0],a[1]+b,b)
  else:t+=int(x),
 return t
lambda p,v:[(['']+p+['']*max(map(abs,r(v))))[x]for x in r(v)]

Прямой подход. Пробовал генераторы и рекурсивную версию, но они не могли побить простое для каждого цикла. Я играю в гольф, так что, скорее всего, это можно немного улучшить. Кроме того, основным недостатком этого фрагмента является то, что диапазон как объект списка вычисляется снова каждый раз, когда элемент извлекается из массива символов (см. Последнюю строку, понимание списка). Это означает, r(s)что выполняется len(r(s)) + 1раз.

Код Ungolfed:

def find_range(string):
    result = []

    # Split at each AND and look at each element separately
    for element in string.split("AND"):
        # Element is just a number, so add that number to the result list
        if "TO" not in string:
            result += [int(string)]

        # Generate a list with all the values in between the boundaries 
        # (and the boundaries themselves, of course) and append it to the result list
        else:
            boundaries = map(int, string.split("TO"))
            ascending = boundaries[0] < boundaries[1]

            # range(start, stop, step) returns [start, ..., stop - 1], so extend the stop value accordingly
            # range(8, 3, 1) returns just [], so choose respective step (negative for a descending sequence)
            result += range(boundaries[0], boundaries[1] + (1 if ascending else -1), 1 if ascending else -1)

# Make the char array 1-indexed by appending an empty char in 0th position
# Add enough empty chars at the end so too large and negative values won't wrap around
interpret = lambda chars, range_expr: [(['']+chars+['']*max(map(abs, find_range(range_expr))))[x] for x in find_range(range_expr)]

Тестовые случаи:

c = list("Hello World")
print interpret(c, "1 TO 3")
print interpret(c, "5")
print interpret(c, "-10 TO 10")
print interpret(c, "0 AND 2 AND 4")
print interpret(c, "8 TO 3")
print interpret(c, "-300 AND 300")

Выход:

['H', 'e', 'l']
['o']
['', '', '', '', '', '', '', '', '', '', '', 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l']
['', 'e', 'l']
['o', 'W', ' ', 'o', 'l', 'l']
['', '']
1Darco1
источник
Отличный первый ответ! Добро пожаловать в PPCG!
mbomb007
Вы можете воспользоваться просмотром Советы по игре в гольф на Python . Вы можете заменить 2 пробела для двойного отступа одной вкладкой. Вы можете поместить код, следующий за ifтой же строкой, и разделить их точкой с запятой. И удалить пробел в [x] for. Также 1if b else-1может быть заменен либо на, b and 1or-1либо 2*bool(b)-1для сохранения байта.
mbomb007 30.09.16
Благодарность! Я уже посмотрел и (пытался) использовать некоторые из них. Будет еще раз просмотреть все ответы снова позже. :)
1Darco1 30.09.16
И я думаю, что вы можете использовать безымянный lambda, так как это не рекурсивно.
mbomb007
1
t+=[int(x)]canbecomet+=int(x),
Cyoce
3

Groovy ( 99 97 байт)

{n,v->Eval.me("[${n.replaceAll(" TO ","..").replaceAll(" AND ",",")}]").flatten().collect{v[it]}}

Попробуйте это здесь: https://groovyconsole.appspot.com/edit/5155820207603712

Объяснение:

  • .replaceAll(" TO ","..") - Заменить на с традиционным диапазоном.
  • .replaceAll(" AND ", ",") - Заменить все ии с запятой.
  • "[${...}]" - Окружите его обозначением «список» в Groovy.
  • Eval.me(...) - Оценить строку как код Groovy.
  • .flatten() - Свести смесь двумерного массива и одномерного массива в одномерный массив.
  • .collect{v[it]} - Соберите индексы из массива в единую структуру.

Вот решение 115 113 байтов, удаляющее нули из вывода: https://groovyconsole.appspot.com/edit/5185924841340928

Вот 117-байтовое решение, если вы говорите, что оно ДОЛЖНО быть проиндексировано как 1 вместо 0: https://groovyconsole.appspot.com/edit/5205468955803648

Если вы хотите, чтобы я поменял оригинал на байт 113/117, дайте мне знать.

Урна волшебного осьминога
источник
Если я вам не нравлюсь, используя «null» для символов, которых там нет, +5 байт в конце для «-null», который удаляет все нули из набора.
Волшебная урна осьминога
1
Я люблю это умное совпадение
downrep_nation
Я узнал кое-что из этого, хотя никогда не знал, что Groovy Eval.me(...)до сих пор; использование его на практике было бы смехотворно небезопасным, но все же это было бы здорово знать.
Волшебная Урна Осьминога
2

C #, 342 байта

a=>r=>{var s=r.Replace(" AND","").Replace(" TO ","|").Split();int b=a.Length,i=0,n,m,j,o;var c=new List<char>();for(;i<s.Length;){var d=s[i++].Split('|');if(d.Length<2)c.Add(b<(n=int.Parse(d[0]))||n<1?' ':a[n-1]);else{o=(m=int.Parse(d[0]))<(n=int.Parse(d[1]))?1:-1;for(j=m;o>0?j<=n:j>=n;j+=o)c.Add(b<j||j<1?' ':a[j-1]);}}return c.ToArray();};

Безгольфовый метод:

static char[] f(char[] a, string r)
{
    var s=r.Replace(" AND","").Replace(" TO ","|").Split();
    int b=a.Length,i=0,n,m,j,o;
    var c=new List<char>();
    for(;i<s.Length;)
    {
        var d=s[i++].Split('|');
        if(d.Length<2)
            c.Add(b<(n=int.Parse(d[0]))||n<1?' ':a[n-1]);
        else
        {
            o=(m=int.Parse(d[0]))<(n=int.Parse(d[1]))?1:-1;
            for(j=m;o>0?j<=n:j>=n;j+=o)
                c.Add(b<j||j<1?' ':a[j-1]);
        }
    }

    return c.ToArray();
}

Полная программа с тестовыми примерами:

using System;
using System.Collections.Generic;

namespace InterpretLooseRanges
{
    class Program
    {
        static void PrintCharArray(char[] a)
        {
            for (int i=0; i<a.Length; i++)
                Console.Write(a[i]);
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            Func<char[],Func<string,char[]>>f= a=>r=>{var s=r.Replace(" AND","").Replace(" TO ","|").Split();int b=a.Length,i=0,n,m,j,o;var c=new List<char>();for(;i<s.Length;){var d=s[i++].Split('|');if(d.Length<2)c.Add(b<(n=int.Parse(d[0]))||n<1?' ':a[n-1]);else{o=(m=int.Parse(d[0]))<(n=int.Parse(d[1]))?1:-1;for(j=m;o>0?j<=n:j>=n;j+=o)c.Add(b<j||j<1?' ':a[j-1]);}}return c.ToArray();};

            char[] ar = {'H','e','l','l','o',' ','W','o','r','l','d'};

            PrintCharArray(f(ar)("1 TO 3"));
            PrintCharArray(f(ar)("5"));
            PrintCharArray(f(ar)("-10 TO 10"));
            PrintCharArray(f(ar)("0 AND 2 AND 4"));
            PrintCharArray(f(ar)("8 TO 3"));
            PrintCharArray(f(ar)("-300 AND 300"));
        }
    }
}

Наивное решение, использующее список символов, который использует ' 'пустой символ и выполняет свою работу. Надеемся на улучшение в ближайшее время.

adrianmp
источник
2

Скала, 165 байт

(s:String,r:String)=>r split "AND"map(_ split "TO"map(_.trim.toInt))flatMap{case Array(a,b)=>if(a<b)a to b else a to(b,-1)
case x=>x}map(i=>s lift(i-1)getOrElse "")

Объяснение:

(s:String,r:String)=> //define a function
r split "AND"         //split the range expression at every occurance of "AND"
map(                  //map each part...
  _ split "TO"          //split at to
  map(                  //for each of these splitted parts, map them to...
    _.trim.toInt          //trim all whitespace and parse as an int
  )                    
)                     //now we have an Array[Array[Int]]
flatMap{              //map each inner Array...
  case Array(a,b)=>if(a<b)a to b else a to(b,-1) //if it has two elements, create a Range
  case x=>x             //otherwise just return it
}                     //and flatten, so we get an Array[Int]
map(i=>               //for each int
  s lift(i-1)         //try to get the char with index i-1, since Scala is zero-based
  getOrElse ""        //otherwise return ""
) 
corvus_192
источник
2

Python 2, 156 155 байт

Мой ответ имеет некоторые подобные идеи, 1Darco1 в ответ , но, используя другой подход с самого начала (строка нарезки , а не списков), то в конечном итоге совсем немного короче. Это было бы на четыре байта короче, если бы 0-индексация была разрешена.

s,r=input()
o=""
for x in r.split("AND"):
    i=map(int,x.split("TO"));d=2*(i[0]<i[-1])-1
    for _ in-1,0:i[_]-=11**9*(i[_]<0)
    o+=s[i[0]-1:i[-1]+d-1:d]
print o

Попробуйте онлайн

К счастью, я могу разобрать строки, содержащие пробелы, в целые числа. Отрицательное индексирование в индексах Python с конца строки, поэтому я использую i[-1]либо то же самое, i[0]либо второе значение, если оно есть. Затем я должен настроить любые отрицательные значения диапазона на более отрицательные, чтобы они не мешали нарезке. Умножение отрицательных значений на 11**9(2357947691 ) учитывает диапазоны, используя целочисленное минимальное значение. Затем просто нарежьте строку, используя обратный срез, если диапазон перевернут.

С нулевой индексацией (151 байт):

s,r=input()
o=""
for x in r.split("AND"):
    i=map(int,x.split("TO"));d=2*(i[0]<i[-1])-1
    for _ in-1,0:i[_]-=11**9*(i[_]<0)
    o+=s[i[0]:i[-1]+d:d]
print o
mbomb007
источник
Молодец! Нарезка определенно является способом пойти сюда. Мой rangeподход - это просто супер многословная форма именно этого. И ты даже избавился от всей if"T"in x: else:роли. +1
1Darco1
2

R, 142 байта

Предполагая, что я правильно понял задачу, здесь я предполагаю, что rэто предопределенное предложение диапазона в строковом формате и что входной массив («Hello world», в примерах) читается из stdin.

r=eval(parse(t=paste("c(",gsub("AND",",",gsub("TO",":",r)),")")))
o=strsplit(readline(),e<-"")[[1]][r[r>0]]
o[is.na(o)]=e
c(rep(e,sum(r<1)),o)

Некоторые тестовые случаи:

r="1 TO 3"
[1] "H" "e" "l"

r="5"
[1] "o"

r="-10 TO 10"
[1] ""  ""  ""  ""  ""  ""  ""  ""  ""  ""  ""  "H" "e" "l" "l" "o" " " "w" "o" "r" "l"

r="0 AND 2 AND 4"
[1] ""  "e" "l"

r="8 TO 3"
[1] "o" "w" " " "o" "l" "l"

r="-300 AND 300"
[1] "" ""

Ungolfed / объяснил

Строка 1

r=eval(parse(t=paste("c(",gsub("AND",",",gsub("TO",":",r)),")")))

R имеет хороший инфиксный оператор, :который генерирует последовательности. 1:5дает [1, 2, 3, 4, 5], и 0:-2дает [0, -1, -2]. Итак, мы заменим предложение TOв свободном диапазоне на :.

                                         gsub("TO",":",r)

Интерпретация AND- это просто объединение. Для этого мы можем использовать функцию c, которая легко может принимать произвольное количество аргументов, разделенных запятыми. Таким образом, мы заменим ANDна,

                          gsub("AND",",",      ...       )

а затем обернуть все это в c(, ).

               paste("c(",              ...               ,")")

Это дает строку символов, которая может выглядеть следующим образом c( 1 : 5 , 7 ). Мы призываем parseпреобразовать в тип «выражение», а затем evalоценить выражение. Получившаяся последовательность чисел затем переназначается переменной r.

r=eval(parse(t=                     ...                        ))

Линия 2

o=strsplit(readline(),e<-"")[[1]][r[r>0]]

Теперь для уродливой части - работа со строками в R, которая быстро запутывается. Сначала мы определяем eпустую строку (она понадобится нам позже).

                      e<-""

Мы читаем из stdin и преобразуем строку символов в массив отдельных символов, разбивая пустую строку. (Например, мы переходим от «Hi» к [«H», «i»].) Это возвращает список длины 1, поэтому мы должны запросить первый элемент, [[1]]чтобы получить массив, с которым мы можем работать. Тьфу, я предупреждал тебя, это было грязно.

  strsplit(readline(), ... )[[1]]

Индексы R начинаются с 1 и имеют приятную особенность с отрицательными числами. Предположим, xесть ['a', 'b', 'c']. Звонок x[1]неудивительно возвращается 'a'. Вызов x[-1]возвращает все , x кроме индекса 1, то есть ['b', 'c']. Это классная функция, но это означает, что мы должны быть осторожны с нашими отрицательными показателями для этой проблемы. Итак, сейчас мы просто возвращаем элементы входного массива с индексом >0и присваиваем результат o.

o=               ...             [r[r>0]]

Линия 3

Однако есть проблема! Для индексов, которые больше, чем длина массива, R просто возвращает NAзначения. Нам нужно это вернуть пустые строки. Таким образом , мы переопределять элементы , oдля которых is.na(o)это TRUEбудет пустая строка.

o[is.na(o)]=e

Строка 4

c(rep(e,sum(r<1)),o)

Наконец, как мы можем справиться с отрицательными (и нулевыми) индексами? Им всем нужно вернуть пустую строку, поэтому мы повторяем пустую строку N раз, где N - это количество индексов <1.

  rep(e,sum(r<1))

Наконец, мы объединяем ранее определенный oв этот (потенциально пустой) список.

c(      ...      ,o)
rturnbull
источник
2

JavaScript (ES6), 141

Безымянная функция с 2 параметрами, первый из которых представляет собой массив символов (также может быть строкой), а второй - строку, содержащую определение диапазона.

Возвращаемым значением является массив, в котором каждый элемент может быть либо одним символом, либо значением js undefined. При строковом преобразовании это приводит к последовательности символов, разделенных запятыми, с неопределенным значением, показанным как «пустой» символ - как тестовые примеры в первой версии вопроса.
Используя .joinвы можете получить строковый результат, похожий на результат теста в текущей версии вопроса.

(l,r,u,z=[])=>(r+' E').replace(/\S+/g,x=>x>'T'?u=t:x>'A'?[...Array((t-(w=(u=u||t)-(d=+u<t?1:-1)-1)-1)*d)].map(_=>z.push(l[w+=d]),u=0):t=x)&&z

Меньше гольфа

(
 l, r, // input paramaters, array/string and string
 u,    // local variable start at 'undefined'
 z=[]  // local variable, will contain the ouput
) => 
  (r+' E') // add an end marker
  .replace( /\S+/g, x=> // execute for each nonspace substring
    x > 'T' // check if 'TO'
    ? u = t // if 'TO' save first value in u (it's a string so even 0 is a truthy value)
    : x > 'A' // check if 'AND' or 'E'
      ? (
          u = u||t, // if u is falsy, it's a single value range t -> t
          d = +u < t ? 1 :-1, // direction of range up or down,comparison has to be numeric, so the leading +
          w = u - d - 1, // starting value (decrement by 1 as js array are 0 based)
          u = 0, // set u to falsy again for next round
          [...Array((t - w - 1) * d)] // build the array of required number of elements
          .map(_ => z.push(l[w+=d])) // iterate adding elements of l to z
        )
      : t = x // if not a keyword, save value in t
  ) && z // return output in z

Тестовое задание

f=
(l,r,u,z=[])=>(r+' E').replace(/\S+/g,x=>x>'T'?u=t:x>'A'?[...Array((t-(w=(u=u||t)-(d=+u<t?1:-1)-1)-1)*d)].map(_=>z.push(l[w+=d]),u=0):t=x)&&z

function run(x)
{
  R.value=x;
  O.textContent=f(L.value,x)
}

run("10 TO 5 AND 5 TO 10")
<table>
<tr><td>Base string</td><td><input id=L value="Hello World"></td></tr>
<tr><td>Custom range</td><td><input id=R ><button onclick='run(R.value)'>-></button></td></tr>
<tr><td>Output</td><td><pre id=O></pre></td></tr>
<tr><td>Test case ranges</td><td>
<select id=T onchange='run(this.value)'>
<option/>  
<option value="1 TO 3">1 TO 3 =&gt; {'H','e','l'}</option>
<option value="5">5 =&gt; {'o'}</option>
<option value="-10 TO 10">-10 TO 10 =&gt; {'','','','','','','','','','','','H','e','l','l','o',' ','W','o','r','l'}</option>
<option value="0 AND 2 AND 4">0 AND 2 AND 4 =&gt; {'','e','l'}
"8 TO 3" => {'o','W',' ','o','l','l'}</option>
<option value="-300 AND 300">-300 AND 300 =&gt; {'',''}</option>
<option value="1 TO 3 AND 3 TO 1">1 TO 3 AND 3 TO 1 =&gt; "HelleH"</option>
<option value="-20 TO 0 AND 1 AND 4">-20 TO 0 AND 1 AND 4 =&gt; "Hl"</option>
</select>
</td></tr>
</table>

edc65
источник
1

Perl - 110 байт

Вызов сценария в командной строке со строкой в ​​качестве первого аргумента и диапазоном в качестве второго.

for(split AND,pop@ARGV){$_>0?print+(split//,"@ARGV")[$_-1]:0for(/(.+)TO(.+)/?($1>$2?reverse$2..$1:$1..$2):$_)}

Де-запутанный:

for $subrange (split 'AND', $ARGV[1]) {
    for $index ($subrange =~ /(.+)TO(.+)/
        ? ($1 > $2 ? reverse $2..$1 : $1..$2) # All indices of that range
        : $subrange) # Otherwise, an index only
    {
        if ($index > 0) {
            # Here, 'split' returns an array of all characters
            print((split //, $ARGV[0])[$index - 1]);
        }
    }
}
Максим Бернард
источник
1

Python 2, 146 байт

lambda s,a:[a[i]for x in[map(int,c.split('TO'))for c in s.split('AND')]for i in range(x[0]-1,x[-1]-2*(x[-1]<x[0]),1-2*(x[-1]<x[0]))if 0<=i<len(a)]

Все тесты в идеоне

Разбивает предложение sна «И», разбивает каждое из результирующих подпунктов на «ТО», преобразует результирующие строки в intиспользование map. Каждый из результатов будет иметь 1 или 2 элемента (1, если в подпункте «TO» не было).
Создает диапазоны на основе 0 для каждого из них, используя параметр шага диапазона как 1 или -1, проверяя значения в индексах 0 и -1 (список с одной записью имеет эту запись в обоих индексах).
Выполняет эти диапазоны и создает список выходных данных, если предоставленные индексы находятся в диапазоне ( if 0<=i<len(a)).

Джонатан Аллан
источник
0

Желе , 28 27 25 байт

œṣ⁾TOj”rV
œṣ“Ñþ»Ç€Ff⁹J¤ị⁹

TryItOnline (также будет работать со строкой вместо массива char)

Как?

œṣ⁾TOj”rV - Link 1, construct sub-range: subclause
  ⁾TO     - string "TO"
œṣ        - split on sublists
     j    - join with
      ”r  - character "r"
        V - evaluate as Jelly code
                (r is the Jelly dyad for inclusive range, which works just like TO
                 when no r is present the string evaluates to the number:
                 " -20 "       evaluates to -20;
                 " -20 r -19 " evaluates to [-20,-19];
                 " 3 r -3 "    evaluates to [3,2,1,0,-1,-2,-3]; etc.)

œṣ“Ñþ»Ç€Ff⁹J¤ị⁹ - Main link: range clause, array 
  “Ñþ»          - compression of the string "AND"
œṣ              - split on sublists
      ǀ        - call the last link (1) as a monad for each
        F       - flatten list
            ¤   - nilad and link(s) as a nilad
          ⁹     - right argument (the array)
           J    - range for length [1,2,...,len(array)]
         f      - filter keep
                      (Jelly indexing is modular so keep only in-bound indexes)
             ị⁹ - index into the array
Джонатан Аллан
источник
0

Clojure 232 230 229 байт

О, какой монстр я создал ... Но на самом деле это было 260, когда я собирался представить его.

Изменить: убрал пробел из #(get r %_""), (if_(< f t)и (take-nth 2_%)(обозначен как _).

(let[S clojure.string/split](fn[r s](apply str(map #(get r %"")(mapcat #(apply(fn([f][(dec f)])([f t](if(< f t)(range(dec f)t)(reverse(range(dec t)f)))))%)(map #(mapv read-string(take-nth 2%))(map #(S % #" ")(S s #" AND "))))))))

Меньше гольфа:

(def f (let[S clojure.string/split]
         (fn[r s] (->> (map #(S % #" ") (S s #" AND "))
                       (map #(mapv read-string (take-nth 2 %)))
                       (mapcat #(apply(fn
                                        ([f][(dec f)])
                                        ([f t](if (< f t)
                                                (range (dec f) t)
                                                (reverse (range (dec t) f)))))  %))
                       (map #(get r % ""))
                       (apply str)))))

Используется clojure.string/splitдля разделения на "И" и "",take-nth отбрасывает «TO» между целыми числами, сопоставление аргументов функции обрабатывает случай с 1 или 2 аргументами, и это все.

Соглашение о вызовах: (f "Hello World" "1 TO 3 AND 2 AND 8 TO 2")

NikoNyrh
источник
Вы можете удалить большое количество байтов, удалив пробелы в целом, особенно между #символами.
clismique
Вы уверены, что я мог бы удалить пробел между любыми #? Я пробовал безуспешно, он "сливается" с предыдущим токеном. О, еще одно место, чтобы удалить, прежде чем %там.
NikoNyrh