Правило великого пальця для великої літери


30

Відповідно до цього сайту, загальним правилом, рекомендованим Посібником зі стилістики уряду США, є

Використовуйте великі літери у заголовках публікацій та документах, крім a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor.

Це може бути неправдою, оскільки я не можу знайти таку рекомендацію в Посібнику стилів , але давайте все-таки використовувати це правило.


Змагання

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

  • Перше і останнє слово з великої літери.
  • Усі інші слова з великої літери, крім a , an , the , at , by , for , in , of , on , to , up , і , as , but , or , and nor .

Рядок введення міститиме щонайменше одне слово, і кожне слово містить принаймні одну букву та лише символи від aдо z.

Це виклик для гольфу з кодом, тому намагайтеся використовувати якомога менше байтів мовою, яку ви обрали. Ви можете написати повну програму або функцію для виконання завдання.

Тестові шафи

"the rule of thumb for title capitalization" -> "The Rule of Thumb for Title Capitalization"
"programming puzzles and code golf" -> "Programming Puzzles and Code Golf"
"the many uses of the letter a" -> "The Many Uses of the Letter A"
"title" -> "Title"
"and and and" -> "And and And"
"a an and as at but by for in nor of on or the to up" -> "A an and as at but by for in nor of on or the to Up"
"on computable numbers with an application to the entscheidungsproblem" -> "On Computable Numbers With an Application to the Entscheidungsproblem"

1
Чи слід починати / закінчувати слова з великої літери, навіть якщо вони є у списку виключень? Ваші приклади говорять так, але специфіка просто говорить з великої літери, якщо їх немає в списку, і нічого про перше / останнє слово. Зауважте, що дві можливості виразно відрізняються, одна - простий фільтр, а друга вимагає особливої ​​поведінки у (буквальних) крайових випадках.
CAD97

3
@ CAD97 Правилами з великої літери є дві точки кулі, а не Котировка. І перший пункт кулі говорить "Перше і останнє слово з великої літери". а другий говорить "Усі інші слова з великої літери, крім ...", означаючи перше і останнє слова завжди з великої літери.
Лайконі

Я пропустив це, якось. Все ж, дякую за уточнення.
CAD97

Я не впевнений, що дійсно потрібно вказати, що кожне слово містить хоча б одну букву. :)
Девід Конрад

Відповіді:


11

Python 2, 118 байт

Подивися ма, без регексу!

for w in`input()`.split():print[w.title(),w][`w`in"'a'an'and'as'at'the'by'but'for'nor'in'of'on'or'to'up'"].strip("'"),

Введення має бути обговорене лапками. На виході є пробіл, що відкладається, і ніякий трейлінг нового рядка (я припускаю, що це нормально). Перевірте всі тестові випадки на Ideone .

Пояснення

Візьмемо вхід a or an як наш приклад.

Використовуючи `x`ярлик Python 2 для repr, ми обертаємо введення в єдині лапки:'a or an' . Потім ми розбиваємося на пробіл і перебираємо слова.

Всередину петлі беремо repr ще раз . Для перших і останніх слів це дає "'a"і "an'". Іншими словами, це дає 'or'. Ми хочемо уникати великих літер, якщо вони відповідають останньому шаблону і є у списку коротких слів. Таким чином, ми можемо представити список слів як рядок "'a'an'...'up'"і знати, щоrepr будь-яке коротке слово буде підрядком.

`w` in "..."надає булеве значення, яке ми можемо розглядати як 0або 1з метою індексації до списку [w.title(), w]. Коротше кажучи, ми називаємо слово з великої літери, якщо воно знаходиться на початку, наприкінці чи не в списку коротких слів. Інакше ми залишаємо це в спокої. На щастя, title()все ще працює, як очікувалося, з введенням як 'a.

Нарешті, ми знімаємо будь-які цитати зі слова та друкуємо їх з пробілом.


8

05AB1E , 68 61 байт

Збережено 7 байт завдяки Аднану

™ð¡Dg<UvyN__NXQ_“a€¤€€€›€‹€‡€†€‚€‰€„€¾€ƒ€œ€³€—š¯“#™yå&&il})ðý

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

Пояснення

“a€¤€€€›€‹€‡€†€‚€‰€„€¾€ƒ€œ€³€—š¯“це рядок словника, перекладений як a an the at by for in of on to up and as but or nor.

™                          # title case input string
ð¡                         # split on spaces
Dg<U                       # store index of last word in X

vy                         # for each word
  N__                      # is it not first index?
     NXQ_                  # is it not last index
         “...“             # the compressed string 
              #            # split on spaces
               ™           # convert to title case
                yå         # is current word in this list?
                  &&       # and the 3 previous conditions together
                    il     # if all are true, convert to lower case
                      }    # end loop
)ðý                        # wrap stack in list and join by spaces

2
Це ніколи не перестає дивувати мене, що вам вдається досягти за допомогою короткої струни абсолютно нерозпізнаних персонажів. Схоже, це працює тоді :) +1
ElPedro

Ба! Я настільки близький, і я не можу знайти спосіб виголити персонажа.
mbomb007

@ mbomb007: Краще поспішіть, перш ніж Jelly, MATL або якась інша мова, яка може застосовувати функції до індексів, побийте це :) Я, здається, пам’ятаю і мову зі стислим регулярним виразом, але не можу згадати, як вона називалася. Це достатньо довго, щоб воно все ще могло бути зіграно.
Емінья


@Adnan: Я почав так, але тільки з 3-знакових слів (які закінчилися довше), але я не розглядав можливість приймати 2-знакові слова ... aзамість того, щоб €…зберігати додатковий байт, а також, якщо вести з ним :) Дякую!
Емінья

7

GNU sed 81 74 73 Байт

Включає +1 для -r

s/\b./\u&/g
:;s/.(And?|A[st]?|The|By|But|[FN]or|In|O[fnr]|To|Up) /\L&/;t

Перший рядок складається з великої літери першої літери кожного слова. Другий перемикає всі необхідні слова назад в малі регістри.

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


6

Сітківка, 69 66 байт

Пропишіть велику літеру кожного слова, а потім змініть вибрані слова на малі, якщо вони не є першим чи останнім словом. У кінці останнього рядка є пробіл.

T`l`L`\b.
+T`L`l` (And?|A[st]?|The|By|But|[FN]or|In|O[fnr]|To|Up) 

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

Це також працює .замість першого місця.

Є багато реджексів однакової довжини, але я не можу знайти спосіб її обрізати ...


(Цей підхід також становить 69 байт у Pip, але я не можу використовувати +трюк, щоб скоротити його.)
DLosc

@DLosc Дякую Айк, чому я цього не бачив. Я був поруч.
mbomb007

3

JavaScript (ES6), 141 138 135 133 байт

Збережено 3 байти завдяки mbomb007

s=>s.replace(/(\w+)( ?)/g,(a,w,n,i)=>i&&n&&/^(a[nst]?|the|by|in|of|on|to|up|and|but|[fn]?or)$/.exec(w)?a:a[0].toUpperCase()+a.slice(1))

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


3

Желе , 58 байт

“Ð/ṃƇ¬þṄẊƙ€,⁽ṙƬ®OṪJ"ɦ3×kf3Ṙç%ġu’b26ịØaṣ”z
e€¢¬T;2Ḷ¤
ḲŒtǦK

СпробуйтеItOnline! або запустити всі тести

Як?

Стислий рядок з пробілами, що розділяють слова, був би 47байтами, розділяючи їх на 1байти, на 48байти.

Два нерозділених стислих рядка слів довжини 2і 3(з 'a' на кінці одного) відповідно були б 40байтами плюс, 2щоб розділити кожен і 1приєднати їх до 45байтів.

Одне базове число 250, як описано нижче, - це 32байти, потім 3перетворити в базу 26, 3індексувати в алфавіт з малих літер і 3розділити його на невикористаний символ 'z', на 41байти.

Отже, пошук слів з великої літери:
“Ð/ṃƇ¬þṄẊƙ€,⁽ṙƬ®OṪJ"ɦ3×kf3Ṙç%ġu’
сформувався так:

Візьміть ці слова і з'єднайте їх з роздільником:
s="a an the at by for in of on to up and as but or nor"

Наступна мітка 'a'як 1, 'b'як і 2з роздільником як 0:

alpha = ' abcdefghijklmnopqrstuvwxyz'
x = [alpha.index(v) for v in s]
x
[1,0,1,14,0,20,8,5,0,1,20,0,2,25,0,6,15,18,0,9,14,0,15,6,0,15,14,0,20,15,0,21,16,0,1,14,4,0,1,19,0,2,21,20,0,15,18,0,14,15,18]

Перетворіть це в базовий 26номер (остання використана літера 'y'плюс цифра для роздільника, код Python для цього:
n=sum(v*26**i for i,v in enumerate(x[::-1]))

Перетворіть це в базовий 250номер (використовуючи список для цифр):

b=[]
while n:
    n,d = divmod(n,250)
    b=[d]+b
b
[16,48,220,145,8,32,202,209,162,13,45,142,244,153,9,80,207,75,35,161,52,18,108,103,52,205,24,38,237,118]

Знайдіть символи в цих індексах на кодовій сторінці желе:

codepage = '''¡¢£¤¥¦©¬®µ½¿€ÆÇÐÑרŒÞßæçðıȷñ÷øœþ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQR TUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~¶°¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ƁƇƊƑƓƘⱮƝƤƬƲȤɓƈɗƒɠɦƙɱɲƥʠɼʂƭʋȥẠḄḌẸḤỊḲḶṂṆỌṚṢṬỤṾẈỴẒȦḂĊḊĖḞĠḢİĿṀṄȮṖṘṠṪẆẊẎŻạḅḍẹḥịḳḷṃṇọṛṣṭụṿẉỵẓȧḃċḋėḟġḣŀṁṅȯṗṙṡṫẇẋẏż«»‘’“”'''
r=''.join(codepage[i-1] for i in b)
r
'Ð/ṃƇ¬þṄẊƙ€,⁽ṙƬ®OṪJ"ɦ3×kf3Ṙç%ġu'

(зауважте: оскільки реальна реалізація є бієктивною, якщо б bбули якісь 0цифри, потрібно було б перенести спочатку)

Решта:

ḲŒtǦK - Main link: title string
Ḳ      - split on spaces
    ¦  - apply to indexes
   Ç   -     given by calling the last link (1) as a monad (with the split title string)
 Œt    -     title case (first letter of each (only) word to upper case)
     K - join on spaces

e€¢¬T;2Ḷ¤ - Link 1, find indexes to capitalise: split title string
e€        - is an element of, for €ach
  ¢       - the result of calling the last link (2) as a nilad
   ¬      - logical not
    T     - get the truthy indexes (indexes of words that are not in the list)
     ;    - concatenate with
        ¤ - nilad followed by link(s) as a nilad
      2Ḷ  - range(2) -> [0,1]
                (we always want to capitalise the first index, 1, and the last index, 0)

“Ð/ṃƇ¬þṄẊƙ€,⁽ṙƬ®OṪJ"ɦ3×kf3Ṙç%ġu’b26ịØaṣ”z - Link 2, make the word list: no arguments
“Ð/ṃƇ¬þṄẊƙ€,⁽ṙƬ®OṪJ"ɦ3×kf3Ṙç%ġu’          - the base 250 number
                                b26       - convert to base 26
                                   ị      - index into
                                    Øa    - lowercase alphabet
                                      ṣ   - split on
                                       ”z - literal 'z' (the separator 0 indexes into `z`)

2

PHP, 158 байт

10 байт, збережених @Titus

foreach($w=explode(" ",$argv[1])as$k=>$v)echo" "[!$k],$k&&$k+1<count($w)&&preg_match("#^(a[snt]?|and|[fn]or|up|by|but|the|to|in|o[rnf])$#",$v)?$v:ucfirst($v);

Попередня версія PHP, 174 байт

foreach($w=explode(" ",$argv[1])as$k=>$v)$k&&$k+1<count($w)&&in_array($v,[a,an,the,at,by,"for",in,of,on,to,up,"and","as",but,"or",nor])?:$w[$k]=ucfirst($v);echo join(" ",$w);

Відлуння в циклі економить 10 байт:foreach(...)echo" "[!$k],(condition)?$v:ucfirst($v);
Тіт

2

TI-Basic, 295 + 59 + 148 = 502 байти

Тепер ви можете використовувати великі рахунки на своєму калькуляторі. Чудово підходить для школи :)

Основна програма, 295 байт

В основному, хитрість у поєднанні слів, щоб все Aне сталося, a- це укладати пробіли, наприклад замінити " A "на " a ". Це також автоматично робить це так, що перше і останнє слова залишаються з великої літери, оскільки вони не мають місця з обох сторін і, таким чином, не будуть відповідати жодному зі слів. (Геніальний, правда? І дуже довгий, бо малі літери - це два байти в кожному ...)

"("+Ans+")→Str1
"@A ~ a@An ~ an@The ~ the@At ~ at@By ~ by@For ~ for@In ~ in@Of ~ of@On ~ on@To ~ to@Up ~ up@And ~ and@As ~ as@But ~ but@Or ~ or@Nor ~ nor@→Str2
For(I,2,length(Ans
If "@"=sub(Str2,I-1,1
Then
" "+Str1+"~"+sub(Str2,I,inString(Str2,"@",I)-I)+" "
prgmQ
Ans→Str1
End
End

Підпрограма ( prgmQ), 59 байт:

Ans→Str9
inString(Ans,"~
sub(Str9,Ans,length(Str9)-Ans+1→Str8
Str9
prgmR
Repeat Str9=Ans+Str8
Ans+Str8→Str9
prgmR
End

Підпрограма ( prgmR), 148 байт:

Ans→Str0
inString(Ans,"~→Z
inString(Str0,"~",Ans+1→Y
inString(sub(Str0,1,Z-1),sub(Str0,Z+1,Ans-Z-1→X
sub(Str0,1,-1+inString(Str0,"~
If X
sub(Str0,1,X-1)+sub(Str0,Y+1,length(Str0)-Y)+sub(Str0,X+length(sub(Str0,Z+1,Y-Z-1)),Z-X-length(sub(Str0,Z+1,Y-Z-1

PS ~представляє маркер 0x81і @являє собою маркер 0x7F, дізнайтеся більше тут .


2

Java 7, 271 259 258 байт

String c(String x){String a[]=x.split(" "),s=" ",r=w(a[0])+s;for(int i=0,l=a.length-1;i<l;r+=(!s.matches("^(a[nst]?|the|by|in|of|on|to|up|and|but|[fn]?or)$")|i==l?w(s):s)+" ")s=a[++i];return r;}String w(String w){return(char)(w.charAt(0)-32)+w.substring(1);}

Невикористаний і тестовий код:

Спробуйте тут.

class M{
  static String c(String x){
    String a[] = x.split(" "),
           s = " ",
           r = w(a[0]) + s;
    for(int i = 0, l = a.length-1; i < l; r += (!s.matches("^(a[nst]?|the|by|in|of|on|to|up|and|but|[fn]?or)$") | i == l
                                                 ? w(s)
                                                 : s)   + " "){
      s = a[++i];
    }
    return r;
  }

  static String w(String w) {
    return (char)(w.charAt(0) - 32) + w.substring(1);
  }

  public static void main(String[] a){
    System.out.println(c("the rule of thumb for title capitalization"));
    System.out.println(c("programming puzzles and code golf"));
    System.out.println(c("the many uses of the letter a"));
    System.out.println(c("title"));
    System.out.println(c("and and and"));
    System.out.println(c("a an and as at but by for in nor of on or the to up"));
    System.out.println(c("on computable numbers with an application to the entscheidungsproblem"));
  }
}

Вихід:

The Rule of Thumb for Title Capitalization 
Programming Puzzles and Code Golf 
The Many Uses of the Letter A 
Title 
And and And 
A an and as at but by for in nor of on or the to Up 
On Computable Numbers With an Application to the Entscheidungsproblem 

1

Гровий, 131 129

Два байти збережено завдяки carusocomputing

{it.split()*.with{a->a in "a an the at by for in of on to up and as but or nor".split()?a:a.capitalize()}.join(" ").capitalize()}

Приємно, я був у 137; ти виграв. Видаліть його i->та використовуйте itдля збереження 2 байт. {it.split()*.with{a->a in "a an the at by for in of on to up and as but or nor".split()?a:a.capitalize()}.join(" ").capitalize()}
Magic Octopus Urn

1
Я не знаю Groovy, але чи справді це з великої букви перше і останнє слово?
Емінья

@Emigna остаточне написання великої літери, починаючи з одного зі слів.
Magic Octopus Urn

@Emigna не дуже, я пропустив цю вимогу (це останнє слово потрібно використовувати з великої літери). Мені потрібно було б відрегулювати свою прихильницю.
Кшиштоф Атласік

Два способи використання .capitalize()займають багато байт. Чи існує короткий шлях, до якого можна зробити псевдонім .capitalize()?
Кіос

1

C #, 305 байт

Ще багато місця для вдосконалення, але ось тут:

s=>{;var b=s.Split(' ');b[0]=((char)(b[0][0]-32))+b[0].Substring(1);int i=0,n=b.Length;for(;++i<n;)if(!"a,an,the,at,by,for,in,of,on,to,up,and,as,but,or,nor".Split(',').Contains(b[i]))b[i]=((char)(b[i][0]-32))+b[i].Substring(1);b[n-1]=((char)(b[n-1][0]-32))+b[n-1].Substring(1);return string.Join(" ",b);};

1

Рубі, 123 117 111 102 байт

->s{s.gsub(/ .|^./,&:upcase).gsub(/ (A[nts]?|The|By|In|To|Up|And|But|[NF]or|O[rnf])(?= )/,&:downcase)}

Вибачте за всі зміни - це має бути останнє.


1

Пітон, 177 байт

Поставляється у функціональному форматі для збереження байтів. Це не особливо конкурентоспроможна відповідь, але вона не вимагає repr()і не regexхитрує. Він також є версійно-агностичним; він працює з Python 2 або 3.

Хоча це, можливо, дуже правильне рішення.

def t(s):
 w="a an the the at by for in of on to up and as but or nor".split()
 l=[(s.title(),s)[s in w]for s in s.split()]
 for x in(0,-1):l[x]=l[x].title()
 return' '.join(l)

1

PHP, 109 142 байти

<?=preg_replace_callback("# (A[snt]?|And|[FN]or|Up|By|But|The|To|In|O[rnf])(?= )#",function($m){return strtolower($m[0]);},ucwords($argv[1]));

Злиття відповіді користувача59178 та mbomb007 .

великі літери першої літери кожного слова, потім нижні регістри всіх слів зі списку, оточені пробілами.
На жаль, зворотний виклик повинен працювати на комплекті; це коштує 29 байт.


це працює не дляa an and as at but by for in nor of on or the to up
Йорг Гюльсерманн

1

Ракетка 353 байти

(define(cap i)(set! i(string-append i))(define c(string-ref i 0))(string-set! i 0(if(char-upper-case? c)c(integer->char(-(char->integer c)32))))i)
(let*((ex(list"a""an""the""at""by""for""in""of""on""to""up""and""as""but""or""and""nor"))(sl(string-split s)))
(string-join(for/list((i sl)(n(in-naturals)))(cond[(= n 0)(cap i)][(member i ex)i][(cap i)]))))

Безголовки:

(define (f s)

  (define (cap i)                 ; sub-fn to capitalize first letter of a word
    (set! i (string-append i))
    (define c (string-ref i 0))
    (string-set! i 0
                 (if (char-upper-case? c)
                     c
                     (integer->char (-(char->integer c)32))))
    i)

  (let* ((ex (list "a""an""the""at""by""for""in""of""on""to""up""and""as""but""or""and""nor"))
         (sl (string-split s)))
    (string-join
     (for/list
         ((i sl)
          (n (in-naturals)))
       (cond
         [(= n 0) (cap i)]
         [(member i ex) i]
         [(cap i)]
         )))))

Тестування:

(f "the rule of thumb for title capitalization")

Вихід:

"The Rule of Thumb for Title Capitalization"

1

Java 7, 431 317 311 байт

Завдяки @KevinCruijssen для 114 байт.
Завдяки @RosLup за збереження 6 байт.

String c(String s){String v="",x,l[]=s.split(" "),b[]={"a","an","the","at","but,"by","for","in","of","on","to","‌​up","as","or","and","nor"};int i=0,f=0,z=0;for(String c:l){for(f=0;f<b.length;z=c.equals(b[f++])|z>0?1:0);x=(char)(c.charAt(0)-32)+c.substring(1);v+=(z>0?i<1|i>l.length-2?x:c:x)+" ";i++;}return v;}

неозорий

Перша відповідь вище 250 байт

 static String c(String s) {
      String v = "", x, l[] = s.split(" "),
b[]={"a","an","the","at","by","for","in","of","on","to",
                                         "‌​up","and","as","or","nor","but"};
    int i , f , z = i = f = 0;
    for (String c : l) {

   for (f = 0; f < b.length; z = c.equals( b[f++] ) | z > 0 ? 1 : 0);
        x = (char)(c.charAt(0) - 32) + c.substring(1);

        v += (z > 0 ? i < 1 | i > l.length - 2 ? x : c : x) + " ";
        i++;
   }
    return v;
    }

1
Це було занадто багато, щоб узагальнити у коментарі, але ви можете пограти в це: String f(String s){String v="",x,l[]=s.split(" "),b[]={"a","an","the","at","by","for","in","of","on","to","up","and","as","but","or","and","nor"};int i=0,f=0,z=0;for(String c:l){for(f=0;f<b.length;z=c.equals(b[f++])|z>0?1:0);x=(char)(c.charAt(0)-32)+c.substring(1);v+=z>0?i<1|i++==l.length-1?x:c:x)+" ";}return v;}( 314 байт ) Я пропоную поглянути на те, що я змінив як поради для наступного разу. :) PS: Я опублікував відповідь з іншим підходом ( 259 байт ).
Кевін Круїйсен

1
Especially things like c.substring(0,1).toUpperCase()+c.substring(1,c.length())+" " which you did twice should make you think about re-using it somehow. And combined initializations like you did correctly with the int, but for some reason not with the String. Also, no need for the extra boolean when you can store at as an int 0 or 1 and then check it >0. And I would try to avoid brackets and break as much as possible; usually there is a trick to get rid of them, like the for(f=0;f<b.length;z=c.equals(b[f++])|z>0?1:0); I've showed. :)
Kevin Cruijssen

1
So much to learn and thanks for being helpful always (long live Nederland ;)
Numberknot

1
Oh, I've made a copy-paste error.. It should be this String c(String s){String v="",x,l[]=s.split(" "),b[]={"a","an","the","at","by","for","in","of","on","to","up","and","as","but","or","and","nor"};int i=0,f=0,z=0;for(String c:l){for(f=0;f<b.length;z=c.equals(b[f++])|z>0?1:0);x=(char)(c.charAt(0)-32)+c.substring(1);v+=(z>0?i<1|i++>l.length-2?x:c:x)+" ";}return v;} And no problem. :) I also learned a lot when I was new to code-golfing. I just make a list with every general codegolf tip I learn and look/update it sometimes. But my code still gets golfed by others a lot.
Kevin Cruijssen

1
In the string b[] there are 2 'and' is that ok?
RosLuP

1

PHP, 117 118 112 bytes

<?=strtr(ucwords(preg_replace("# (?=(a[snt]?|and|[fn]or|up|by|but|the|to|in|o[rnf]) )#","!",$argv[1])),'!',' ');

Uses the behaviour of ucwords() and escapes the relevant words that are surrounded by spaces then deletes the escape characters.

I copied the (a[snt]?|and|[fn]or|up|by|but|the|to|in|o[rnf]) from Jörg Hülsermann's answer but as the approach is completely different I'm posting it as a separate answer.

edit: bug noticed by Titus, fixing it cost 1 byte. also: 6 bytes saved thanks to his helpful comment about strtr


Save 6 bytes with strtr instead of str_replace. Or prepend the words with <> and drop the str_replace and use HTML output.
Titus

In some cases you can use preg_filter instead of preg_replace. I have not try it with your solution
Jörg Hülsermann

The regex will not work for two words from the list in a row; test nice try for a start. Replacing one of the spaces with an assertion solves that (+4 bytes).
Titus

Unfortunately preg_filter would fail on the title test case, returning nothing.
user59178

1

Pure bash - 253

(no external programs called) - needs bash v4

declare -A b;for x in A An The At By For In Of On To Up And As But Or Nor;do b[$x]=1;done
while read -a w;do
n=${#w[@]};o[0]=${w[0]^}
for((i=1;i<n-1;i++)){
g=${w[$i]^};((${b[$g]}))&&o+=(${g,,})||o+=($g);}
((n>1))&&o[$n]=${w[-1]^}
echo ${o[@]};o=()
done

normal view with comments

#create the "blacklist"
declare -A b
for w in A An The At By For In Of On To Up And As But Or Nor
do
    b[$x]=1
done

# logic:
# read each line (split by words) into array
# and each word is assigned capitalized to the new output array
# but the blacklisted ones

#read each line to array w (split on spaces)
while read -a w
do
    n=${#w[@]}         # get the number of words
    o[0]=${w[0]^}          # copy the capitalized word1
    for((i=1 ; i<n-1 ; i++)) { # loop over 2 up to last -1 words

        g=${w[$i]^}    # for the given word
        # check if it is in the blacklisted ones
        # if yes - convert to lowercase, if not leave as it is
        # and append to the output array
        (( ${b[$g]} )) && o+=(${g,,}) || o+=($g)
    }
    # capitalize the last word if here is more words
    (( n>1 )) && o[$n]=${w[-1]^}
    # make a line from the words
    echo ${o[@]}
    o=() #cleanup
done

output

Title
And and And
The Rule of Thumb for Title Capitalization
Programming Puzzles and Code Golf
The Many Uses of the Letter A
A an and as at but by for in nor of on or the to Up
On Computable Numbers With an Application to the Entscheidungsproblem

1

Japt, 71 bytes

£`a  e  by f     up d  ¿t  n`¸aX >0©Y¦0©YĦZl ?X:Xg u +XÅ}S

Try it online!

Explanation:

£`a  e  by f     up d  ¿t  n`¸aX >0©Y¦0©YĦZl ?X:Xg u +XÅ}S
£`...`qS aX >0&&Y!=0&&Y!=UqS l -1?X:Xg u +Xs1}S

£                                            }S   // Split at spaces and map each item X by this function:
 `...`                                            //  Backticks are used to decompress strings
      qS                                          //  Split the decompressed string at spaces.
         aX >J                                    //  If this contains X
              &&Y!=0                              //  and the index is non-zero (it's not the first word)
                    &&Y!=UqS l -1                 //  and the index is not the length of the input -1 (it's not the last word),
                                 ?X               //  return X.
                                   :Xg u +Xs1     //  Else, return X capitalized. (Literally X[0].toUpperCase() + X.slice(1))
                                             }S   // Rejoin with spaces

One of my favorite Japt features is its string compression, which uses the shoco library.

You can compress a string by wrapping it in Oc"{string}"Oc"a an the at by for in of on to up and as but or nor"

Then decompressing it with backticks or Od"{compressed string}"Od"a e by f up d ¿t n"


The -S flag was added after this challenge was posted, so your current solution is non-competing. However, I think you can do £...+XÅ}S, which would be competing for the same byte-count (Try it online!)
ETHproductions

How does shoco compare with Jelly's dictionary compression in your opinion?
Robert Fraser

@RobertFraser Compared to Jelly, it's not very good at compressing strings of English words, but it is very good at compressing strings of arbitrary lowercase letters, which comes in handy sometimes.
ETHproductions

1

Pure bash - 205 192 181 bytes

tc(){
while read -a x
do x=(${x[@]^})
for ((i=1;i<${#x[@]}-1;i++))
do
case "${x[i]}" in
A|A[nts]|The|By|[FN]or|In|O[fnr]|To|Up|And|But)x[i]=${x[i],};;
esac
done
echo ${x[@]}
done
}

Like jm66's answer tc accepts standard input.


0

Actually, 79 bytes

' ,ÿsd@p@`;0"A0An0The0At0By0For0In0Of0On0To0Up0And0As0But0Or0Nor"síu'ù*ƒ`Moq' j

Try it online!

Explanation:

' ,ÿsd@p@`;0"longstring"síu'ù*ƒ`Moq' j
' ,ÿs                                   title case input, split on spaces
     d@p@                               pop first and last words to stack
         `;0"longstring"síu'ù*ƒ`M       for every word except the first and last:
          ;0"longstring"s                 duplicate word, split the long string on 0s
                         íu               1-based index of word in list (0 if not found)
                           'ù*            "ù"*(index)
                              ƒ           execute the resulting string as a function (lowercases word if it's in the list)
                                 oq' j  put the first and last word back in the list, join with spaces

0

Batch, 323 bytes

@echo off
set s=
for %%w in (@%*@)do call:w %%w
echo%s%
exit/b
:w
for %%s in (a an the at by for in of on to up and as but or nor)do if %%s==%1 set s=%s% %1&exit/b
set w=%1
set w=%w:@=%
set f=%w:~0,1%
for %%c in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)do call set f=%%f:%%c=%%c%%
set s=%s% %f%%w:~1%

With comments:

@echo off
rem Start with an empty output string
set s=
rem Wrap the parameters in @ signs to identify the first and last words 
for %%w in (@%*@) do call :w %%w
rem Ignore the leading space when printing the result
echo%s%
exit/b
:w
rem Check whether this is a word that we don't change
for %%s in (a an the at by for in of on to up and as but or nor) do if %%s==%1 set s=%s% %1&exit/b
set w=%1
rem Delete any @ signs from the first and last words
set w=%w:@=%
rem Get the first character
set f=%w:~0,1%
rem Case insensitively replace each upper case letter with itself
for %%c in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do call set f=%%f:%%c=%%c%%
rem Concatenate with the rest of the word
set s=%s% %f%%w:~1%
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.