Друкуйте відносний шлях


15

Опис

Враховуючи шлях джерела та шлях призначення, виведіть відносний шлях до пункту призначення щодо джерела.

Правила

  1. Вхід може надходити зі stdin або як аргумент до програми / функції.

  2. Потрібно підтримувати і контури стилів Windows, і Unix.

  3. Шлях виводу може використовуватись /та / або \для роздільника шляху (ваш вибір і комбінація обох є ОК).

  4. Ви можете припустити, що відносний шлях можливий.

  5. Заборонено використання зовнішніх програм, вбудованих або бібліотечних функцій для обчислення відносних шляхів (наприклад, Python's os.path.relpath)

  6. Це

    Редагувати: нове правило з коментарів.

  7. Відносний шлях повинен бути найкоротшим можливим відносним шляхом.

  8. Припустимо, шлях призначення відрізняється від вихідного шляху.

Приклад 1

# In
/usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin

# Out
../../vim/vim73/ftplugin

Приклад 2

# In
C:\Windows\System32\drivers
C:\Windows\System32\WindowsPowerShell\v1.0

# Out
..\WindowsPowerShell\v1.0

Щодо правила №3 - це суміш гаразд? Напр ../../vim\vim73\ftplugin.
Дункан Джонс

1
Чи потрібно повертати найкоротший відносний шлях або це нормально, щоб пройти якийсь шлях?
Говард

@Duncan Так, суміш добре.
Рюнант

1
@Howard, це повинен бути найкоротший відносний шлях.
Рюнант

чи не повинен бути перший приклад ../vim/vim73/ftplugin?
Martijn

Відповіді:


2

CJam, 46 байт

ll]{'\/'/f/:~}/W{)__3$=4$@==}g@,1$-"../"*o>'/*

Спробуйте в Інтернеті.

Приклади

$ echo '/usr/share/geany/colorschemes
> /usr/share/vim/vim73/ftplugin' | cjam path.cjam; echo
../../vim/vim73/ftplugin
$ echo 'C:\Windows\System32\drivers
> C:\Windows\System32\WindowsPowerShell\v1.0' | cjam path.cjam; echo
../WindowsPowerShell/v1.0

Як це працює

ll]         " Read two lines from STDIN and wrap them in an array.                       ";
{           " For each line:                                                             ";
  '\/       " Split by “\”.                                                              ";
  '/f/      " Split each chunk by “/”.                                                   ";
  :~        " Flatten the array of chunks.                                               ";
}/          "                                                                            ";
W           " Push -1 (accumulator).                                                     ";
{           "                                                                            ";
  )__       " Increment and duplicate twice.                                             ";
  3$=       " Extract the corresponding chunk from the first line.                       ";
  4$@=      " Extract the corresponding chunk from the second line.                      ";
  =         " If the are equal,                                                          ";
}g          " repeat the loop.                                                           ";
@,          " Rotate the array of chunks of the first line on top and get its length.    ";
1$-         " Subtract the value of the accumulator.                                     ";
"../"*o     " Print the string “../” repeated that many times.                           ";
>           " Remove all chunks with index less than the accumulator of the second line. ";
'/*         " Join the chunks with “/”.                                                  ";

1
У ньому є помилка. Спробуйте /aa/xз /ab/y.
jimmy23013

@ user23013: виправлено.
Денніс

2

Bash + coreutils, 116

Ось сценарій оболонки, щоб отримати кульку. Досить впевнений, що буде коротших відповідей:

n=`cmp <(echo $1) <(echo $2)|grep -Po "\d+(?=,)"`
printf -vs %`grep -o /<<<${1:n-1}|wc -l`s
echo ${s// /../}${2:n-1}

Вихід:

$ ./rel.sh /usr/share/geany/colorschemes /usr/share/vim/vim73/ftplugin
../vim/vim73/ftplugin
$ ./rel.sh /usr/share/geany/colorschemes/ /usr/share/vim/vim73/ftplugin/
../../vim/vim73/ftplugin/
$ ./rel.sh /usr/share/vim/vim73/ftplugin /usr/share/geany/colorschemes
../../geany/colorschemes
$ 

Зверніть увагу, що сценарій не може визначити, чи є рядок ftpluginфайлом чи каталогом. Ви можете явно надати каталог, додавши до нього знак, /як у наведеному вище прикладі.

Не оброблять контури, що містять пробіли чи інші смішні символи. Не впевнений, це вимога чи ні. Буде потрібно лише кілька додаткових цитат.


2

Javascript (E6) 104

Редагувати Додано попередження для виводу

R=(s,d)=>alert(s.split(x=/\/|\\/).map(a=>a==d[0]?d.shift()&&'':'../',d=d.split(x)).join('')+d.join('/'))

Безумовно

R (s,d) => // a single espression is returned, no {} or () needed
  s.split(x=/\/|\\/) // split string at / or \, save regexp in X for later
  .map( // create a new array from s
     a => a == d[0] // check if current of s and d equals
          ? d.shift() && '' // map to '' and cut 1 element of d
          : '../', // else map to '../'
     d=d.split(x)) // second param of map is useless, so split d here
  .join('')+d.join('/') // join map and concat to rest of d adding separators

Тест

R('C:\\Windows\\System32\\drivers','C:\\Windows\\System32\\WindowsPowerShell\\v1.0')

../WindowsPowerShell/v1.0

R('/usr/share/geany/colorschemes','/usr/share/vim/vim73/ftplugin')

../../vim/vim73/ftplugin


2

Рубін> = 1,9, 89 94 символів

$;=/\\|\//
a,b=$*.map &:split
puts"../"*(a.size-r=a.index{a[$.+=1]!=b[$.]}+1)+b[r..-1]*?/

Введіть аргументи командного рядка. Працює як для стилів UNIX, так і для Windows, включаючи шляхи з повторними іменами папок:

$ ruby relpath.rb /usr/share/geany/colorschemes /usr/share/vim/vim73/ftplugin
../../vim/vim73/ftplugin
$ ruby relpath.rb 'C:\Windows\System32\drivers' 'C:\Windows\System32\WindowsPowerShell\v1.0'
../WindowsPowerShell/v1.0
$ ruby relpath.rb /foo/bar/foo/bar /foo/qux/foo/bar
../../../qux/foo/bar

2

J - 63 char

Функція, що займає старий шлях зліва та новий шлях праворуч.

}.@;@(c=.c&}.`(,~(<'/..')"0)@.(~:&{.))&('/'<;.1@,'\/'&charsub)~

Це рішення складається з трьох частин, схожий post@loop&pre~. Пояснюється вибухом:

post @ loop & pre ~   NB. the full golf
                  ~   NB. swap the arguments: new on left, old on right
            & pre     NB. apply pre to each argument
       loop           NB. run the recursive loop on both
post @                NB. apply post to the final result

'/'<;.1@,'\/'&charsub  NB. pre
         '\/'&charsub  NB. replace every \ char with /
'/'     ,              NB. prepend a / char
   <;.1@               NB. split string on the first char (/)

c=.c&}.`(,~(<'/..')"0)@.(~:&{.)  NB. loop
                      @.(~:&{.)  NB. if the top folders match:
    &}.                          NB.   chop off the top folders
   c                             NB.   recurse
       `                         NB. else:
           (<'/..')"0            NB.   change remaining old folders to /..
         ,~                      NB.   append to front of remaining new folders
c=.                              NB. call this loop c to recurse later

}.@;  NB. post
   ;  NB. turn the list of folders into a string
}.@   NB. chop off the / in the front

Зауважте, що ми додаємо ведучий /до кожного шляху перед розбиттям, щоб ми обробляли шляхи у стилі Windows, роблячи C:«папку». Це призводить до появи порожньої папки на початку контурів у стилі Unix, але це завжди видаляється циклом.

Дивіться це в дії:

   NB. you can use it without a name if you want, we will for brevity
   relpath =. }.@;@(c=.c&}.`(,~(<'/..')"0)@.(~:&{.))&('/'<;.1@,'\/'&charsub)~
   '/usr/share/geany/colorschemes' relpath '/usr/share/vim/vim73/ftplugin'
../../vim/vim73/ftplugin
   'C:\Windows\System32\drivers' relpath 'C:\Windows\System32\WindowsPowerShell\v1.0'
../WindowsPowerShell/v1.0

Ви також можете спробувати самі на tryj.tk .


2

Баш, 69 66

Я цього не публікував, тому що думав, що хтось повинен мати можливість це зробити набагато краще. Але, мабуть, це не так просто.

sed -r 'N;s/(.*[/\])(.*)\n\1/\2\n/'|sed '1s/[^/\]*/../g;N;s!\n!/!'

Nз’єднує sedдві лінії разом. Перший вираз видаляє загальний префікс, що закінчується на /або \. Другий вираз замінює імена каталогів ..у першому рядку. Нарешті, він з'єднує дві лінії з роздільником.

Дякуємо Гастуркуну за 3 символи.


Виглядає цікаво! Чи можете ви додати пояснення?
Цифрова травма

1
@DigitalTrauma Додано Але в основному це просто регулярні вирази.
jimmy23013

Спасибі! Я буду грати з цим наступного разу, коли я буду на терміналі
Digital Trauma

Вам не потрібно запускати sedдвічі, ви можете це зробити за допомогою одного сценарію.
Hasturkun

@Hasturkun Але я не знайшов способу з цим працювати N. Можливо, ви можете відредагувати цю відповідь, якщо знаєте як.
jimmy23013

1

C, 119 106

void p(char*s,char* d){while(*s==*d){s++;d++;}s--;while(*s){if(*s==47||*s==92)printf("../");s++;}puts(d);}

p(char*s,char*d){for(;*s;)*s++-*d?*s-47||printf("../"):d++;puts(d);}68 символів без зворотної косої риси
bebe

Спасибі! Але правило 2 говорить, що обидва повинні бути підтримані. Саме на виході я можу вибрати те чи інше (правило 3).
kwokkie

1

Пітон 3, 120

a,b=(i.split('\\/'['/'in i])for i in map(input,'  '))
while[]<a[:1]==b[:1]:del a[0],b[0]
print('../'*len(a)+'/'.join(b))

Приклад:

$ python3 path.py
 /usr/share/geany/colorschemes
/usr/share/vim/vim73/ftplugin 
../../vim/vim73/ftplugin

Чи може бути коротший спосіб виконувати рядок 1 з execопераціями з рядком?
xnor

@xnor Можливо, але я не бачу цього.
grc

Може map(input,' ')працювати для `(input (), input ())? (Я не можу це перевірити)
xnor

@xnor Так, це працює спасибі!
гр.ч.

1

Рубі - 89

r=/\/|\\/
s = ARGV[0].split r
d = ARGV[1].split r
puts ("../"*(s-d).size)+((d-s).join"/")

Використання:

ruby relative.rb working/directory destination/directory

3
Це не виконується для аргументів , як /foo/bar/foo/barі /foo/qux/foo/bar.
Вентеро

І виходить з ладу для шляхів стилів для Windows
edc65

@ edc65 У правилах не вказано, що потрібно підтримувати обидва формати шляху, ви можете зробити будь-який.
nderscore

@nderscore Правило 2 Потрібно підтримувати і контури стилів Windows, і Unix.
edc65

1
@Jwosty: Ну, це краса, чи не так? Придумати рішення, яке є коротким і правильним. У мене в минулому були випадки, коли мені довелося повністю переглянути відповідь через пропущений крайовий випадок. Тепер я в цьому випадку частково ставлю на себе завдання, тому що я вважаю, що кожне завдання має супроводжувати суцільний набір тестових справ, але добре.
Joey

0

JavaScript - 155

function p(s,d){s=s.split(/\\|\//g);d=d.split(/\\|\//g);o=[];for(i=0;s[i]==d[i];i++);for(j=s.length-i;j--;)o[j]="..";return o.concat(d.slice(i)).join("/")}

Розбирає або формат шляху, але виводить з /роздільником.

console.log(p("/usr/share/geany/colorschemes","/usr/share/vim/vim73/ftplugin"));
../../vim/vim73/ftplugin
console.log(p("/usr/share/geany/colorschemes/test/this","/usr/share/vim/vim73/ftplugin/this/is/a/test"));
../../../../vim/vim73/ftplugin/this/is/a/test
console.log(p("c:\\windows\\system32\\drivers\\etc\\host","c:\\windows\\system\\drivers\\etc\host"));
../../../../system/drivers/etchost

0

PHP, 158 151

function r($a,$b){$a=explode('/',$a);$b=explode('/',$b);foreach($a as $k=>$v){if($v==$b[$k])$b[$k]='..';else break;}unset($b[0]);echo implode('/',$b);}

Безголівки:

function r($a,$b){
    $a=explode('/',$a);
    $b=explode('/',$b);
    foreach($a as $k=>$v){
        if($v==$b[$k])$b[$k]='..';
        else break; 
    }
    unset($b[0]);
    echo implode('/',$b);
}
// these lines are not included in count:
r('/test/test2/abc','/test/test3/abcd'); // ../test3/abcd
r('/test/test2/abc','/test/test2/abcd'); // ../../abcd

Ваша відповідь не правильна. Спробуйте зробити цей бруд і cdсформувати один до одного :)
core1024

0

Groovy - 144 ч

Одне рішення:

x=args[0][1]!=':'?'/':'\\'
f={args[it].tokenize x}
s=f 0
d=f 1
n=0
while(s[n]==d[n++]);
u="..$x"*(s.size-(--n))
println "$u${d.drop(n).join x}"

Приклад виведення:

bash$ groovy P.groovy C:\\Windows\\System32\\drivers C:\\Windows\\System32\\WindowsPowerShell\\v1.0
..\WindowsPowerShell\v1.0

bash$ groovy P.groovy /usr/share/geany/colorschemes /usr/share/vim/vim73/ftplugin
../../vim/vim73/ftplugin

bash$ groovy P.groovy /foo/bar/foo/bar /foo/qux/foo/bar
../../../qux/foo/bar

неозорений:

// fs = file seperator, / or \
fs = args[0][1]!=':'?'/':'\\'

s = args[0].tokenize fs
d = args[1].tokenize fs

// n = # of matching dirs from root + 1
n = 0
while (s[n] == d[n++]) ;

// up = the up-prefix. e.g. "../../..", for instance 
n--
up = "..${fs}" * (s.size-n)

println "$up${d.drop(n).join fs}"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.