Генерування єдиного рядка з оксфордською комою зі списку


24

Назвіть декілька розумних (коротких та ідіоматичних) підходів до взяття списку рядків та повернення єдиного правильно прописаного рядка, побудованого зі списку, з кожним цитованим елементом.

Це придумали для мене під час експерименту з Groovy , для якого моє занадто буквальне, але ілюстративне рішення

def temp = things.collect({"\'${it}\'"})
switch (things.size()) {
    case 1:
        result = temp[0]
        break
    case 2:
        result = temp.join(" and ")
        break
    default:
        result = temp.take(temp.size()-1).join(", ") + ", and " + temp[-1]
        break
}

Тобто, ['1']повинен поступатися '1', ['1','2']повинен поступатися '1 and 2', [бачити, що я там робив?] І ['1','2','3']повинен поступатися '1, 2, and 3'.

У мене є кілька хороших відповідей щодо Groovy, але я хотів би побачити, що можуть зробити інші мови.

Які є компактні розумні підходи на різних мовах, які використовують переваги та особливості цих мов?


6
Ласкаво просимо до PPCG. Зазвичай питання, розміщені тут, - це виклики для громади. Як такі, їм потрібні об'єктивні критерії виграшу. Я вважаю, що це питання досить добре відповідає проблемі кодового гольфу . Чи можете ви позначити це як таке? Якщо так, я думаю, вам слід трохи посилити технічні характеристики вводу та виводу.
Digital Trauma

7
Це було б цікавіше з реальними реченнями :['we invited the stripper','JFK','Stalin']
ThisSuitIsBlackNot

1
Чи можна припустити, що самі рядки не містять коми?
Мартін Ендер

2
Виклик мав би мати назву "Хто дає ---- про Оксфордську кому ?"
Ігбі Ларгеман

3
@OldCurmudgeon Я думаю, ти маєш на увазі "американізований сміття";)
ThisSuitIsBlackNot

Відповіді:


76

CSS, 132 116 115 байт

a:not(:last-child):nth-child(n+2):after,a:nth-last-child(n+3):after{content:","}a+:last-child:before{content:"and "

У гольф-коді CSS не надто часто зустрічається, оскільки він може лише форматувати текст, але він справді працює для цього завдання, і я думав, що це буде цікаво зробити. Дивіться це в дії, використовуючи фрагмент, описаний вище (натисніть "Показати фрагмент коду").

Список має бути у зв'язаному HTML-файлі з кожним елементом, оточеним <a>тегами та розділеним перервами рядків. Елементи списку повинні бути єдиними елементами їхнього батьківського елемента, наприклад

<a>one</a>
<a>two</a>
<a>three</a>

Пояснення

a:not(:last-child):nth-child(n+2)::after,
a:nth-last-child(n+3)::after {
    content: ",";
}

a + :last-child::before {
    content: "and ";
}

Розглянемо версію, що не перебуває в нагорі. Якщо ви не знайомі з тим, як працює CSS, все поза фігурними дужками - це селектор, який визначає набір елементів HTML, до яких застосовуються декларації всередині дужок. Кожна пара селектор-декларація називається правилом . (Це складніше, ніж це, але буде достатньо для цього пояснення.) Перед застосуванням будь-якого стилю список з'являється розділеним лише пробілами.

Ми хочемо додавати коми після кожного слова, окрім останнього, за винятком двосписних списків, у яких не виникає коми. Перший селектор, a:not(:last-child):nth-child(n+2):afterвибирає всі елементи, крім першого та останнього. :nth-child(n+2)це коротший спосіб сказати :not(:first-child), і він, в основному, працює, вибираючи елементи, індекс яких (починаючи з 1) більший або дорівнює 2. (Так, це все ще мене трохи бентежить. Документи MDN можуть допомогти.)

Тепер нам просто потрібно вибрати перший елемент, щоб отримати кому, якщо всього три або більше елементів. a:nth-last-child(n+3):afterпрацює як :nth-child, але рахуючи ззаду, тому він вибирає всі елементи, крім двох останніх. Кома приймає об'єднання двох множин, і ми використовуємо :after псевдоелемент для додавання contentвідразу після кожного вибраного елемента.

Друге правило простіше. Нам потрібно додати "і" перед останнім елементом у списку, якщо тільки це не один елемент. Іншими словами, нам потрібно вибрати останній елемент, якому передує інший елемент. +- сусідній селектор сестер у CSS.


1
Блискуче! Я це люблю. : D
COTO

Якби тільки ти користувався <li>.
slebetman

1
<li>було б ідеально, але це додало б двох додаткових символів до селекторів. Я вибрав, <a>тому що це одна літера і не застосовує власне форматування.
NinjaBearMonkey

Нічого ... це дуже вражає. Нагорода за кмітливість.

CSS як головна відповідь? Приємно.
Брендон

13

Haskell: 81, 77 74 символів

f[x]=x
f[x,y]=x++" and "++y
f[x,y,z]=x++", "++f[y++",",z]
f(x:y)=x++", "++f y

Характеристики Haskell: узгодження візерунків


Можна видалити пробіли
промінь

1
Ви можете просто видалитиf[x,y,z]
seequ

І зараз це майже стандартна рекурсія. :)
seequ

ви забули оксфордську коску та пробіли після коми - f["a","b","c"]передбачається, "a, b, and c"але це є"a,b and c"
гордий haskeller

4
як щодо гольфу, замінивши y++", and "++zна f[y++",",z]:)
гордий haskeller

7

Рубін, 47 байт

q=" and ";p$*[2]?(a=$*.pop;$**", "+?,+q+a):$**q

Пояснення

  • Вхідними даними є аргументи командного рядка ( $*).
  • Коли $*є третій елемент ( $*[2]не повертає нуль), візьміть усі елементи мінус останній та перетворіть їх у рядок, розділений комами, використовуючи Array#*. Нарешті додайте додаткову кому, рядок " and "та останній аргумент командного рядка.
  • Коли $*немає третього елемента, наводилися два, один або нульовий аргументи. Аргументи можна сміливо поєднувати з рядком " and "і давати правильний результат.

6

Пітон 2 (61)

s=input()
d=s.pop()
print", ".join(s)+", and "[8-7*len(s):]+d

Основна хитрість - відрізати частину кінцевого столяра ", and "для одного та двох елементів. Для одного все вирізається, а для двох - кома видаляється. Це робиться шляхом нарізки [8-7*len(s):](зауваживши, що sце на один коротший час після pop).

На жаль, dне можна просто замінити його виразом, або це popстанеться занадто пізно.


Ви могли б замінити перший sз s=input()і видалити перший рядок, якщо я не помиляюся. Економить 2 символи.
tomsmeding

@tomsmeding Я не розумію, що ти пропонуєш. Код посилається на sкілька разів.
xnor

Зачекайте, я дивлюсь речі сьогодні вранці. Забудьте про це. :)
tomsmeding

6

CSS, 62 символи 112 символів

Натхненний іншими записами, ще коротший. Зауважте, що потрібно, щоб елементи A не були розділені пробілами:

a+a:before{content:", "}a+a:last-child:before{content:", and "

http://jsfiddle.net/olvlvl/1Ls79ocb/

Виправити "один і два", на що вказував Денніс:

a+a:before{content:", "}a+a:last-child:before{content:", and "}a:first-child+a:last-child:before{content:" and "

http://jsfiddle.net/olvlvl/1Ls79ocb/3/


5

Perl (v 5.10+) - 37 35 34 28

@F>2&&s/ /, /g;s/.* \K/and /

щоб працювати з perl -ape, з список додається простір , відокремлене від STDIN.

Вихід для різних входів:

$ perl -ape '@F>2&&s/ /, /g;s/.* \K/and /'
oxford
oxford
oxford cambridge
oxford and cambridge
oxford cambridge comma
oxford, cambridge, and comma
oxford cambridge comma space
oxford, cambridge, comma, and space

Ви можете зменшити це на 3 байти, змінивши останню заміну наs/(\S+)$/and $1/
ThisSuitIsBlackNot

1
@ThisSuitIsBlackNot Ви можете кинути "\ n" (спасибі), але ви не можете скинути пробіли, або введення "x" стає "і x"
abligh

На жаль, слід перевірити більше входів. У будь-якому випадку, ви забули $якір у вашій редакції, тому вхід foo bar bazстає foo, and bar, baz. Також ви можете видалити один з пробілів, виконавшиs/( \S+)$/ and$1/
ThisSuitIsBlackNot

Мій останній коментар все ще залишає вас у 35, але ви можете написати $#F>1як @F>2поголити ще одного. Вибачте за всі пропозиції щодо мікрополіпшень, мені дуже подобається ця відповідь :)
ThisSuitIsBlackNot

@ThisSuitIsBlackNot зовсім не - мікроедити вітаються. Я таємно змагаюся з 28 байтами CJam і 30 байтами Golfscript. Однак я дістався до 34, не роблячи перекладу вашого коментаря до останнього (тестував echo -n ... | wc -c). Якщо ви пропустите пробіл до (\S+)того, як ви дістанетесь до 33 символів, але якщо ви введете, test thisви отримаєте test and this(два пробіли після test), так що без більше Я думаю, що це неправильно
abligh

4

Javascript (63)

l=a.length;l>2&&(a[l-1]='and '+a[l-1]);a.join(l>2?', ':' and ')

Випадки:

  • a = [1] => 1
  • a = [1, 2] => 1 and 2
  • a = [1, 2, 3] => 1, 2, and 3

Caveat: це змінить останній елемент у масиві довжиною> 2.


2
Гарне використання&&
Кріс Блум,

4

GNU sed - 69 символів, у тому числі 1 для -rпрапора

s/ /, /g
s/( [^ ]+)( [^ ]+)$/\1 and\2/
s/^([^,]+),([^,]+)$/\1 and\2/

Займає розділений пробілом список (досить ідіоматичний для скриптів оболонок).

Приклад

$ sed -r -f oxfordcomma.sed <<< "1"
1
$ sed -r -f oxfordcomma.sed <<< "1 2"
1 and 2
$ sed -r -f oxfordcomma.sed <<< "1 2 3"
1, 2, and 3
$

Так, давайте перейдемо з кодом-гольф .
orome

Другий вихід повинен бути 1 and 2замість1, 2
Оптимізатор

@Optimizer - цілком правильно. Виправлено.
Digital Trauma

3

Пітон 2 - 71, 70 68

s=input()
l=len(s)-1
print', '.join(s[:l])+', and '[l<2:]*(l>0)+s[l]

Кількість символів, включаючи і те, inputі print.


3

Рубін, 57 байт

f=->l{s=l*', ';s.sub(/,(?!.*,)/,(l.size<3?'':?,)+' and')}

Я приєднуюся до рядка з, ,а потім замінюю останню кому в рядку на and(і необов'язковий кома залежно від довжини списку).


3

Кобра - 60

do(l as String[])=l.join(', ',if(l.length<3,'',',')+' and ')

List<of T>.joinФункція Кобри дозволяє вказати інший роздільник для останніх двох елементів списку, і саме це робить це таким коротким.


3

Perl - 59

sub f{$a=join', ',@_;$#_&&substr$a,(@_>2)-3,@_<3,' and';$a}

Приєднується до списку комами, тоді, якщо у списку є більше одного елемента, або додає ' and'після останньої коми (якщо довжина> = 3), або замінює останню кому (якщо довжина == 2).


3

PHP, 192 167 146 136 символів:

$n=' and ';$s=count($a);echo ($s<3)?join($n,$a):join(', ',array_merge(array_slice($a,0,$s-2),Array(join(",$n",array_slice($a,-2,2)))));

На основі функції, яку я написав років тому на http://www.christopherbloom.com/2011/05/21/join-implode-an-array-of-string-values-with-formatting/


Чи можете ви, будь ласка, вписати свій текст (з оксфордськими комами) у текст відповіді?

Так, вибачте. Я був у своєму телефоні, і він не вставив код належним чином. Я оновлюсь з мого робочого столу
Кріс Блум

@ Chrisbloom7, навіть якщо додати тривіально, відповідь має містити робочий код, а не той, який може бути зроблений таким. також, оскільки мета полягає в тому, щоб зробити код якомога меншим, а не розміщення робочого коду робить ваш рахунок неіснуючим.
гордий haskeller

Вибачення знову. Перший плакат в CG. Я виправив свою відповідь
Кріс Блум,

3

Пакетна - 151 байт

@echo off&set f=for %%a in (%~1)do
%f% set/aa+=1
%f% set/ac+=1&if !c!==1 (set o=%%a)else if !c!==!a! (set o=!o!, and %%a)else set o=!o!, %%a
echo !o!

Примітка; Ви повинні викликати скрипт cmdіз встановленим /vперемикачем як on, це так, що я не повинен включати тривалий setLocal enableDelayedExpansionу сценарій. В іншому випадку додайте 30 до кількості байтів і зателефонуйте до сценарію звичайно.

h:\uprof>cmd /von /c test.bat "1 2 3"
1, 2, and 3

h:\uprof>cmd /von /c test.bat "1 2 3 4 5"
1, 2, 3, 4, and 5

3

Groovy, 47 43 57 символів, JavaScript ES6 56 символів

Groovy:

(a[1]?a[0..-2].join(", ")+(a[2]?",":"")+" and ":"")+a[-1]

Оскільки масив заповнений символами, його можна замінити a.size>1наa[1]

JavaScript, ES6:

a.join(', ').replace(/,([^,]+)$/,`${a[2]?',':''} and$1`)

В обох випадках передбачається, що змінна a має відповідний масив.


6
Оксфордська кома - це кома перед сполучником.
Денніс

3

PHP, 86 84 символів

$a = ['one', 'two', 'three', 'four', 'five'];

Після ініціалізації масиву ми починаємо рахувати:

$L=count($a)-1;$S=', ';$L<2?($S=' and '):($a[$L]='and '.$a[$L]);echo implode($S,$a);

Досить:

$last_index = count($a) - 1;
$separator = ', ';
if ($last_index < 2) {
    $separator = ' and ';
} else {
    $a[$last_index] = 'and '.$a[$last_index];
}
echo implode($separator, $a);

Останній пункт у списку змінено. Це повинно бути добре, оскільки в PHP присвоєння масиву та виклики функцій виконують копії.


3

CJam, 35 30 28 27 байт

q~)\_"and "L?@+a+_,2=", ">*

Це програма, яка читає зі STDIN і друкує на STDOUT. Спробуйте в Інтернеті.

Як це працює

q~                                     " Q := eval(input())                               ";
  )                                    " P := Q.pop()                                     ";
   \_"and "L?@+                        " P := (Q ? 'and ' : '') + P                       ";
                a+                     " Q += [P]                                         ";
                  _,2=", ">            " J := ', '[(len(Q) == 2):]                        ";
                           *           " R := J.join(Q)                                   ";
                                       " print R (implicit)                               ";

Приклад виконання

$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1"]'; echo
1
$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1""2"]'; echo
1 and 2
$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1""2""3"]'; echo
1, 2, and 3
$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1""2""3""4"]'; echo
1, 2, 3, and 4

Я на вашому хвості - дивіться нижче :-)
abligh

3

Google Таблиці, 67 68 67 92 байт

=SUBSTITUTE(JOIN(", ",FILTER(A:A,A:A<>"")),",",IF(COUNTA(A:A)>2,",","")&" and",COUNTA(A:A)-1

Введення починається з комірки A1та продовжується вниз, хоча існує багато записів.
JOINоб'єднує їх усіх у рядок із пробілом між комами.
FILTERвидаляє будь-які непорожні місця, щоб ви не закінчилися нескінченними комами в кінці.
SUBSTITUTEзамінює останню кому (виявляється шляхом COUNTAпідрахунку непорожніх входів).

Це не дуже цікаво, але це можливо в одній клітині.


Це виключає оксфордську кому.
Парасолька

@ Umbrella Ну, це було мені дурно, чи не так? +1 байт
інженер Тост

Ви повинні мати можливість відмовитися від терміналу )цієї формули для -1 байт; Добре бовтався, це була найшвидша редакція, яку я коли-небудь бачив +1
Тейлор Скотт

Дві примітки після тестування цього - A:A>""слід перетворити на A:A<>""це, і це не передбачає опущення оксфордської коми через випадки лише двох об'єктів
Тейлор Скотт

@TaylorScott Я думаю, що я коли-небудь перевіряв текст і пропустив проблему >"". Виправлення, яке я зробив раніше, явно не перевірялось. Мені не подобається напрямок, який йшов ...
Інженер Тост

2

JavaScript (ES6) 60

Якщо у вхідному масиві відсутні порожні рядки

f=(a,b=a.pop())=>a[0]?a.join(', ')+(a[1]?',':'')+' and '+b:b

Тест у консолі FireFox / Firebug

console.log(f(['We invited the stripper','JFK','Stalin']))

Вихідні дані

 We invited the stripper, JFK, and Stalin

Як мені це запустити? Я спробував це node --harmonyз масивом струн var a = 'abcdefgh'.split('');, але він просто сидить там із a ...і, здається, нічого не робить. Також +1 за використання унікальної функції ES6 ()=>.
Адріан

@Adrian функція повертає рядок без жодного виводу. Я додам тест у відповідь.
edc65

Я думаю, що f=(a,b=a.pop())=>a[0]?a.join(', ')+', and '+b:bце також правильно і на 13 байт коротше.
Інго Бюрк

1
@ IngoBürk, який вирізає частину, яка обробляє кому, що містить 2 елементи. Вгадай що? З двома пунктами вихід неправильний.
edc65

@ edc65 Ах, д'о. Може погано.
Інго Бюрк

2

JavaScript - 60 56 71 67 63

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

Припускаючи, що масив зберігається у var a:

a.join((a.length>2?',':'')+' ').replace(/([^,\s])$/,"and $1")

Скорочене просто перевірка на показник [2]: (так, булева примус)

a.join((!!a[2]?',':'')+' ').replace(/([^,\s])$/,'and $1')

Мабуть, я смоктав на тестування і пропустив тест з одноразовим вступом. Ось виправлена ​​версія:

a.join((!!a[2]?',':'')+' ').replace(/([^,\s])$/,(!!a[1]?'and ':'')+'$1')

Поголив 2 символи, перевернувши мої булі та ще 3, перемістивши пробіл від конкатенації в тодішній / інший перший термінал:

a.join((!a[2]?' ':', ')).replace(/([^,\s])$/,(!a[1]?'':'and ')+'$1')

Завдяки @tomsmeding за те, що нагадав мені, що мені не доведеться примушувати буленів, тому що JS робить це для мене. Я також зрозумів, що забув видалити круглі дужки, що відокремлюють перший трійник від конкатенації всередині join():

a.join(a[2]?', ':' ').replace(/([^,\s])$/,(a[1]?'and ':'')+'$1')

Чи правильно я це зробив? Обов’язкове нове вибачення у гольфіста.


1
Сподобався вираз, відомий як мазохізм.
seequ

Насправді вам це зовсім не було потрібно !!; a[2]?A:Bпрацює вже. Можливо , доведеться перевернути ваші булеві знову .
Томмедінг

@tomsmeding - ти прав. Я завжди з обережністю ставився до булевої примусової поведінки за замовчуванням JavaScript, тому у мене є звичка ручно примушувати булевих людей ...
Адріан,

@Adrian Correction: будь-яке значення, окрім порожнього рядка, a[i]дорівнює true . Ну, майже. Я припускаю, що якщо ви отримаєте 2 входи, ваш масив має довжину дві. Потім a[2]===undefinedі undefinedоцінює до false. EDIT: ваша редакція - це те, що я мав на увазі :)
tomsmeding

2

Гольфскрипт - 31 39 34 30

Привіт Світ! Я довго ховаюсь, перший раз плакат. Ось моє 30-байтне рішення в Golfscript (враховуючи масив, такий як [1 2 3] вже є в стеку).

.,3<" and ":A{~A(;\+]", "}if*

Результати належні для всіх тестових випадків.

EDIT: Візьміть це, CJam!


Відредаговано для обліку масиву довжиною 1.
Джосія Уінслоу

Відредаговано для більш ефективного алгоритму.
Джосія Уінслоу

Бій триває. : P
Денніс

Ах, але зараз ми рівні. : P
Josiah Winslow

2

Xojo, 52 71 83 символів

dim i as int8=ubound(L)
L(i)=if(i=0,"","and ")+L(i)
Return L.Join(if(i=1," ",", ")

Зауважте, що UBound на один менший, ніж довжина масиву.


Це здається, що додається кома, коли є два елементи - не слід
edc65

Хороший улов. Відредаговано, щоб виправити.
срібло

2

Excel VBA, 108 байт

Анонімна функція негайного вікна VBE, яка приймає введення як масив, обмежений простором, від діапазону A1та виводить до безпосереднього вікна VBE.

x=Split([A1]):y=UBound(x):For i=0To y-2:?x(i)", ";:Next:If y>0Then?x(i)IIf(y>1,",","")" and "x(i+1)Else?[A1]

1

Ракетка 87

(define(f x)(string-join(map ~a x)", "#:before-last(if(= 2(length x))" and "", and ")))

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

1

Пітон 62 символи

Якщо припустити, що i - це список рядків:

(" and", ", and")[len(i) < 2].join(", ".join(i).rsplit(",",1))


1

Джулія (53)

print(join(ARGS,", ",(endof(ARGS)>2?",":"")" and "))

Знімає аргументи з STDIN і виводить в STDOUT

Рішення за допомогою Base.join (items, delim [, last])

Редагувати:

Тестові справи

julia oxfordComma.jl 1

1

julia oxfordComma.jl 1 2

1 і 2

julia oxfordComma.jl 1 2 3

1, 2 і 3


1
Я не знаю Джулії, але це виглядає так, що вона не породжує коду Оксфорда.
flornquake

@flornquake він друкує з оксфордською комою, завдяки необов'язковому "останньому" аргументу
Cruor

2
Це має бути 1, 2, and 3, ні 1, 2 and 3.
flornquake

1

Rant (108)

[$[l:@b]:[r:each][s:[cmp:[rc];2;[is:different;,]]\s][before:[last:[notfirst:and\s]]][sync:;ordered][arg:b]]

Безголівки:

[$[l:@b]:
    [r:each]                            # Repeat for each item
    [s:[cmp:[rc];2;[is:different;,]]\s] # Separate by comma if n > 2
    [before:[last:[notfirst:and\s]]]    # Insert "and" before last item
    [sync:;ordered]                     # Force forward order
    [arg:b]                             # Read list
]

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

[$l:{A|B|C|D|E}]

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


1

Піт, 28

j>", "qlQ2+_t_Q+?"and "tQkeQ

Приклади:

$ pyth programs/oxford.pyth <<< "['1']"
1

$ pyth programs/oxford.pyth <<< "['1','2']"
1 and 2

$ pyth programs/oxford.pyth <<< "['1','2','3']"
1, 2, and 3

Я не можу зрозуміти, як слід форматувати вхід.
Денніс

@Dennis Я додам кілька тестових випадків.
isaacg

Я насправді це намагався, але це не спрацювало з моєю версією Pyth ( TypeError: can only concatenate list (not "str") to list). Це добре працює з останньою версією.
Денніс

@Dennis Так, правило додавання до списку було додано зовсім недавно.
isaacg

1

PHP - 72

Налаштування вводу:

$i=[1,2,3,4,5];

І тоді процес:

$x=count($i)-1;if($x) {$i[$x]=" and ".$i[$x];}echo join($x>1?",":"",$i);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.