タイトルの大文字化の経験則


30

このサイトによると米国政府印刷局スタイルマニュアルが推奨する一般的なルールは

a、an、the、at、by、for、in、of、on、to、up、and、as、but、or、およびnorを除く、出版物および文書のタイトルのすべての単語を大文字にします。

スタイルマニュアルでそのような推奨事項を見つけることができないため、これは真実ではないかもしれませんが、とにかくこのルールを使用しましょう。


チャレンジ

スペースで区切られた小文字の単語で構成される入力文字列が与えられた場合、次の規則に従って文字列の大文字化を出力します

  • 最初と最後の単語は大文字です。
  • aantheatbyforinofontoupandasbutor、およびnorを除くすべての他の単語は大文字です。

入力文字列は、少なくとも一つの単語が含まれ、各単語から少なくとも一つの文字と文字のみが含まれているaz

これはコードゴルフのチャレンジなので、選択した言語でできるだけ少ないバイトを使用するようにしてください。タスクを完了するための完全なプログラムまたは関数を作成できます。

テストケース

"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
除外リストにある場合でも、開始/終了の単語は大文字にする必要がありますか?あなたの例では「はい」と言っていますが、仕様では、単語がリストに含まれていない限り大文字にするだけで、最初/最後の単語については何も記述していません。2つの可能性は明確に異なることに注意してください。1つは単純なフィルターで、2つ目は(リテラル)エッジの場合に特別な動作が必要です。
CAD97

3
@ CAD97大文字の規則は、引用ではなく2つの箇条書きです。そして、最初の箇条書きには、「最初と最後の単語は大文字で始まる」と書かれています。2番目は「...以外のすべての単語は大文字で始まる」と言い、最初と最後の単語は常に大文字になります
ライコニ

どういうわけか、私はそれを逃しました。それでも、明確にしてくれてありがとう。
CAD97

各単語に少なくとも1つの文字が含まれていることを指定する必要があるかどうかはわかりません。:)
デビッドコンラッド

回答:


11

Python 2, 118 bytes

Look ma, no regex!

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("'"),

Input must be wrapped in quotes. Output has a trailing space and no trailing newline (I assume that's okay). Verify all test cases on Ideone.

Explanation

Let's take the input a or an as our example.

Python 2の`x`ショートカットを使用してrepr、入力を単一引用符で囲みます。'a or an'. Then we split on whitespace and iterate over the words.

ループ内で、repr 再びを取得ます。最初と最後の言葉では、これができます"'a""an'"。言い換えれば、それはを与え'or'ます。後者のパターンに適合し、短い単語のリストに含まれる場合、単語の大文字化を避けたい。したがって、単語リストを文字列として表すことができ、短い単語のすべてが部​​分文字列になることが"'a'an'...'up'"わかりreprます。

`w` in "..."ブール値を与えます。これは、リストへのインデックス付けとして、0または1リストへのインデックス付けの目的で扱うことができます[w.title(), w]。要するに、単語が先頭、末尾、または短い単語のリストにない場合は、単語のタイトルを大文字にします。それ以外の場合は、そのままにします。幸いなことに、のtitle()ような入力でも期待どおりに動作します'a

最後に、単語から単一引用符を取り除き、末尾にスペースを入れて出力します。


8

05AB1E, 68 61 bytes

Saved 7 bytes thanks to Adnan

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

Try it online!

Explanation

“a€¤€€€›€‹€‡€†€‚€‰€„€¾€ƒ€œ€³€—š¯“ is a dictionary string translated as 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
It Never Ceases to Amaze Me What You Manage to Achieve With a Short String of Totally Unrecognisible Characters. Looks like it works then :) +1
ElPedro

Bah! I'm so close, and I can't find a way to shave off a character.
mbomb007

@mbomb007: Better hurry up before Jelly, MATL or some other language that can apply functions to indices comes and beat this :) I seem to remember a language with compressed regex as well, but can't remember what it was called. This is long enough that it might still be golfable as well.
Emigna

1
For 62 bytes :)
Adnan

@Adnan: I started like that but only with the 3-char words (which ended up longer), but I didn't consider taking the 2-char words as well... a instead of €… saves an additional byte as well if lead of with it :) Thanks!
Emigna

7

GNU sed 81 74 73 Bytes

Includes +1 for -r

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

The first line capitalizes the first letter of every word. The second switches all of the required words back to lowercase.

Try it Online!


6

Retina, 69 66 bytes

Capitalize the first letter of every word, then change the selected words to lowercase if they're not the first or last word. There's a space at the end of the last line.

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

Try it online

This also works with a . instead of the first space.

There are a lot of regexes with the same length, but I can't find a way to trim it anymore...


(This approach is also 69 bytes in Pip, but I can't use the + trick to shorten it.)
DLosc

@DLosc Thanks. Idk why I didn't see that. I was close.
mbomb007

3

JavaScript (ES6), 141 138 135 133 bytes

Saved 3 bytes thanks to 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))

Test cases


3

Jelly, 58 bytes

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

TryItOnline! or run all tests

How?

A compressed string with spaces separating the words would be 47 bytes, splitting it costs 1 byte, for 48 bytes.

Two unseparated compressed strings of the words of length 2 and 3 (with an 'a' on the end of one) respectively would be 40 bytes plus 2 to split each and 1 to join them, for 45 bytes.

One base 250 number as described below is 32 bytes, then 3 to convert to base 26, 3 to index into the lowercase alphabet and 3 to split it on the unused character, 'z', for 41 bytes.

So, the lookup for the words not to capitalise:
“Ð/ṃƇ¬þṄẊƙ€,⁽ṙƬ®OṪJ"ɦ3×kf3Ṙç%ġu’
was formed like so:

Take those words and join them with a separator:
s="a an the at by for in of on to up and as but or nor"

Next label 'a' as 1, 'b' as 2 with the separator as 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]

Convert this into a base 26 number (the last letter used is 'y' plus a digit for the separator, Python code for this is:
n=sum(v*26**i for i,v in enumerate(x[::-1]))

Convert that into a base 250 number (using a list for the digits):

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]

Lookup the characters at those indexes in jelly's codepage:

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

(note: since the actual implementation is bijective, if b had any 0 digits one would need to carry down first)

The rest:

ḲŒ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 Bytes

10 Bytes saved by @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);

Previous version PHP, 174 Bytes

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);

Echoing in the loop saves 10 bytes: foreach(...)echo" "[!$k],(condition)?$v:ucfirst($v);
Titus

2

TI-Basic, 295 + 59 + 148 = 502 bytes

Now you can capitalize on your calculator. Great for school :)

Main Program, 295 bytes

Basically, the trick to matching words so all A don't become a is to enclose with spaces, such as replace " A " with " a ". This also automatically makes it so that the first and last words stay capitalized, because they do not have a space on both sides and will thus not match any of the words. (Genius, right? And super long because lowercase letters are two bytes each...)

"("+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

Subprogram (prgmQ), 59 bytes:

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

Subprogram (prgmR), 148 bytes:

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

P.S. ~ represents token 0x81 and @ represents token 0x7F, learn more here.


2

Java 7, 271 259 258 bytes

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);}

Ungolfed & test code:

Try it here.

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"));
  }
}

Output:

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

Groovy, 131 129

Two bytes saved thanks to 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()}

Nice, I was at 137; you win. Remove it i-> and use it to save 2 bytes. {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
I don't know Groovy but does this really capitalize the first and last word?
Emigna

@Emigna the final capitalize covers starting with one of the words.
Magic Octopus Urn

@Emigna not really, I missed that requirement (that last word needs to be capitalized). I would need to adjust my anwser.
Krzysztof Atłasik

The two uses of .capitalize() take up a lot of bytes. Is there a short way you can make an alias to .capitalize()?
Cyoce

1

C#, 305 bytes

Lots of room for improvement still but here you go:

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

Ruby, 123 117 111 102 bytes

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

Sorry for all the edits - this should be the last one.


1

Python, 177 bytes

Delivered in function format for byte saving purposes. This is not an especially competitive answer, but it is one that doesn't require repr() or regex trickery. It is also version-agnostic; it works with Python 2 or 3.

Though it is perhaps a very by-the-rules solution.

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 bytes

<?=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]));

A merger of user59178´s and mbomb007´s answer.

uppercases the first letter of every word, then lowercases all words from the list surrounded by spaces.
Unfortunately, the callback has to operate on the complete set; this costs 29 bytes.


it works not for a an and as at but by for in nor of on or the to up
Jörg Hülsermann

1

Racket 353 bytes

(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)]))))

Ungolfed:

(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)]
         )))))

Testing:

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

Output:

"The Rule of Thumb for Title Capitalization"

1

Java 7, 431 317 311 bytes

Thanks to @KevinCruijssen for 114 bytes.
Thanks to @RosLup for saving 6 bytes.

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;}

ungolfed

first answer above 250 bytes

 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
It was too much to summarize in a comment, but you can golf it to this: 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 bytes) I suggest taking a look at what I changed as tips for next time. :) PS: I've posted an answer with a different approach (259 bytes).
Kevin Cruijssen

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%
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.