Znajdź pierwsze słowo zaczynające się od każdej litery

25

Biorąc pod uwagę ciąg znaków, znajdź pierwsze słowo zaczynające się od każdej litery (bez rozróżniania wielkości liter).

Próba

Wykorzystując Ferulas flourish in gorgeous gardens.jako dane wejściowe:

"Ferulas flourish in gorgeous gardens."
 ^^^^^^^          ^^ ^^^^^^^^
 |                |  |
 |                |  --> is the first word starting with `g`
 |                --> is the first word starting with `i`
 --> is the first word starting with `f`

Następnie wynikiem dla tego przykładu powinny być dopasowane słowa połączone jedną spacją:

"Ferulas in gorgeous"

Wyzwanie

Zarówno dane wejściowe, jak i wyjściowe muszą być reprezentacją ciągu lub najbliższą alternatywą w Twoim języku.

Program lub funkcja dozwolona.

Można rozważyć słowo jest co najmniej jedno z następujących: lowercase or uppercase letters, digits, underscore.

To jest , wygrywa najkrótsza odpowiedź w bajtach.

Kolejne próbki:

input: "Take all first words for each letter... this is a test"
output: "Take all first words each letter is"

input: "Look ^_^ .... There are 3 little dogs :)"
output: "Look _ There are 3 dogs"

input: "...maybe some day 1 plus 2 plus 20 could result in 3"
output: "maybe some day 1 plus 2 could result in 3"
oddalony
źródło
Czy dozwolone są spacje końcowe / początkowe? <s> Czy mogę założyć, że słowa są oddzielone jednym spacją w oryginalnym ciągu? </s>
Qwertiy
Zrozumiałem to na podstawie przykładów, więc w komentarzu jest <s> </s>. Co z przycinaniem przestrzeni?
Qwertiy

Odpowiedzi:

17

Siatkówka , 28 bajtów:

M! I` \ b (\ w) (? <! \ B \ 1. +) \ W *
¶
 
  • M! - Dopasuj każdą pracę i wydrukuj wszystkie słowa oddzielone znakiem nowej linii.
  • i - Zignoruj ​​przypadek.
  • \b(\w) - Przechwyć pierwszą literę każdego słowa
  • (?<!\b\1.+)- Po dopasowaniu litery sprawdź, czy nie było poprzedniego słowa zaczynającego się od tej samej litery. \1.+zapewnia co najmniej dwa znaki, więc pomijamy bieżące słowo.
  • \w*- dopasuj resztę słowa.
    Powyższe pasuje tylko do słów - wszystkie pozostałe znaki są usuwane.
  • ¶\n - Zamień znaki nowej linii na spacje.

Wypróbuj online!

Kobi
źródło
9

Siatkówka , 45 bajtów

i` \ b ((\ w) \ w *) \ b (? <= \ b \ 2 \ w * \ b. +)

\ W +
 
^ | $

Po prostu używa pojedynczego wyrażenia regularnego, aby usunąć późniejsze słowa zaczynające się od tego samego \wznaku (bez uwzględniania wielkości liter w iopcji), konwertuje przebiegi \Wdo pojedynczej spacji, a następnie usuwa wszelkie spacje wiodące / końcowe.

Wypróbuj online!

Edycja: Zobacz odpowiedź @ Kobi dla krótszej wersji używającejM!`

Sp3000
źródło
Cholera, ledwo mnie bije! Nie mogłem jednak pojąć wyglądu.
GamrCorps,
3
Dodałem kolejną odpowiedź Retiny - myślę , że to w porządku, jeśli są wystarczająco różne (podstawowa koncepcja jest oczywiście podobna).
Kobi,
1
@Kobi Jest znacznie lepiej, więc cieszę się, że to widzę :) Uświadamia mi, o ile więcej muszę się dowiedzieć o opcjach linii Retiny, a co nie.
Sp3000,
Czy możesz to zrobić, aby zaoszczędzić kilka bajtów? i` \b((\w)\w*)\b(?<=\b\2\w*\b.+)(spacja przed pierwszą \b) Czy linie nie są potem potrzebne?
Leaky Nun
@KennyLau Niestety nie sądzę, że to działa, ponieważ słowa niekoniecznie są oddzielone spacjami, np.a...a -> a
Sp3000,
9

JavaScript (ES6), 73 71 bajtów

s=>s.match(u=/\w+/g).filter(w=>u[n=parseInt(w[0],36)]?0:u[n]=1).join` `

Zaoszczędzono 2 bajty dzięki @ edc65!

Test

var solution = s=>s.match(u=/\w+/g).filter(w=>u[n=parseInt(w[0],36)]?0:u[n]=1).join` `;
var testCases = [
  "Ferulas flourish in gorgeous gardens.",
  "Take all first words for each letter... this is a test",
  "Look ^_^ .... There are 3 little dogs :)",
  "...maybe some day 1 plus 2 plus 20 could result in 3"
];
document.write("<pre>"+testCases.map(t=>t+"\n"+solution(t)).join("\n\n")+"</pre>");

użytkownik 81655
źródło
Używasz parseInt("_",36) = NaN? Bluźnierstwo!
Sp3000,
1
Ciekawostką jest to: działa @ Sp3000
edc65
Używanie u = regexp jest naprawdę sprytne. Zaoszczędź 2 bajtys=>s.match(u=/\w+/g).filter(w=>u[w=parseInt(w[0],36)]?0:u[w]=1).join' '
edc65
@ edc65 Dzięki. Jest to całkiem wygodne, że istnieje 37 możliwych wyjść dla jednej cyfry bazowej 36.
user81655 7.04.16
7

Pyth, 23 bajty

J:z"\w+"1jdxDJhM.grhk0J

Wypróbuj online: pakiet demonstracyjny lub testowy

J:z"\w+"1znajduje wszystkie słowa w danych wejściowych za pomocą wyrażenia regularnego \w+i zapisuje je w J.

.grhk0Jgrupuje słowa według pierwszej małej litery, hMpobiera pierwsze z każdej grupy, xDJsortuje te słowa według indeksu w ciągu wejściowym i jdumieszcza spacje między nimi.

Jakube
źródło
4

Perl 6, 39 bajtów

{.words.grep({!%.{.substr(0,1).lc}++})}
Ven
źródło
1
42 bajtów , które rozwiązuje słów mających dopasować \w+i Golfy ten substrudział
Jo Król
3

C, 142 132 122 bajtów

10 bajtów lżejszych dzięki @tucuxi!

b[200],k;main(c){for(;~c;isalnum(c)|c==95?k&2?:(k|=!b[c|32]++?k&1?putchar(32):0,7:2),k&4?putchar(c):0:(k&=1))c=getchar();}

Drukuje spację po ostatnim słowie wyjściowym.

Mllllbyte
źródło
1
można golić kontrole na c>47i c<58stosując isalnumzamiastisalpha
sotalia amazońska
3

MATL , 23 bajty

'\w+'XXtck1Z)t!=XRa~)Zc

To zapożycza pomysł Jakube'a na użycie wyrażenia regularnego do usuwania niechcianych postaci i dzielenia w tym samym czasie.

Dane wejściowe to ciąg ujęty w pojedyncze cudzysłowy.

Wypróbuj online!

Wyjaśnienie

'\w+'XX  % find words that match this regexp. Gives a cell array
t        % duplicate
c        % convert into 2D char array, right-padded with spaces
k        % make lowercase
1Z)      % get first column (starting letter of each word)
t!=      % duplicate, transpose, test for equality: all combinations  
XR       % set diagonal and below to 0
a~       % true for columns that contain all zeros       
)        % use as a logical index (filter) of words to keep from the original cell array
Zc       % join those words by spaces
Luis Mendo
źródło
2

Vim 57 klawiszy

:s/[^a-zA-Z_ ]//g<cr>A <cr>ylwv$:s/\%V\c<c-v><c-r>"\h* //eg<c-v><cr>@q<esc>0"qDk@q

Wyjaśnienie:

:s/[^a-zA-Z_ ]//g                                 #Remove all invalid chars.
A <cr>                                            #Enter insert mode, and enter 
                                                  #a space and a newline at the end
ylwv$:s/\\c%V<c-v><c-r>"\h* //eg<c-v><cr>@q<esc>  #Enter all of this text on the 
                                                  #next line

0                                                 #Go to the beginning of the line
"qD                                               #Delete this line into register
                                                  #"q"
k@q                                               #Run "q" as a macro  

#Macro
ylw                                               #Yank a single letter
   v$                                             #Visual selection to end of line
     :s/                                          #Substitute regex
       \%V\c                                      #Only apply to the selection and 
                                                  #ignore case
            <c-v><c-r>"                           #Enter the yanked letter
                       \h*                        #All "Head of word" chars
                                                  #And a space
                           //                     #Replace with an empty string
                             eg                   #Continue the macro if not found
                                                  #Apply to all matches
                               <c-v><cr>          #Enter a <CR> literal
                                        @q<esc>   #Recursively call the macro

Naprawdę jestem rozczarowany, jak długo to trwa. Te znaki „nieważny” (wszystko ale a-z, A-Z, _i spacja) naprawdę rzucił mnie. Jestem pewien, że jest lepszy sposób:

:s/[^a-zA-Z_ ]//g

Ponieważ \hwszystkie mecze tego oczekują od miejsca, ale nie mogę wymyślić, jak ustawić metachar w zasięgu. Jeśli ktoś ma jakieś wskazówki, chciałbym je usłyszeć.

DJMcMayhem
źródło
3
dlaczego a-zA-Z_nie \w? cyfry są prawidłowe
edc65
2

Julia, 165 155 151 129 102 bajtów

g(s,d=[])=join(filter(i->i!=0,[(c=lcfirst(w)[1])∈d?0:(d=[d;c];w)for w=split(s,r"\W",keep=1<0)])," ")

Ta funkcja przyjmuje łańcuch i zwraca łańcuch.

Nie golfowany:

function g(s, d=[])
    # Split the string into an array on unwanted characters, then for
    # each word, if the first letter has been encountered, populate
    # this element of the array with 0, otherwise note the first letter
    # and use the word. This results in an array of words and zeros.
    x = [(c = lcfirst(w)[1])  d ? 0 : (d = [d; c]; w) for w = split(s, r"\W", keep=1<0)]

    # Remove the zeros, keeping only the words. Note that this works
    # even if the word is the string "0" since 0 != "0".
    z = filter(i -> i != 0, x)

    # Join into a string and return
    return join(z, " ")
end

Zaoszczędź 53 bajty dzięki pomocy Sp3000!

Alex A.
źródło
2

Galaretka, 32 31 bajtów

ØB;”_
e€¢¬œṗf€¢¹ÐfµZḢŒlQi@€$ịj⁶

Wypróbuj online!

Dennis
źródło
2

C # (LINQPAD) - 136 128 bajtów

var w=Util.ReadLine().Split(' ');string.Join(" ",w.Select(s=>w.First(f=>Regex.IsMatch(""+f[0],"(?i)"+s[0]))).Distinct()).Dump();
jzm
źródło
2

05AB1E , 40 bajtów

Kod:

94L32+çJžj-DU-ð¡""Kvy¬Xsl©åï>iX®«Uy}\}ðý

Wypróbuj online!

Wyjaśnienie:

Najpierw generujemy wszystkie znaki, które należy usunąć z ciągu wejściowego za pomocą 94L32+ç( Wypróbuj tutaj ). Łączymy ten ciąg za pomocą Ji usuwamy, [a-zA-Z0-9_]który jest przechowywany w žj ( spróbuj tutaj ). Usuwamy wszystkie znaki znajdujące się w drugim ciągu z pierwszego ciągu, który nas opuści:

!"#$%&'()*+,-./:;<=>?@[\]^`{|}~

Można to również przetestować tutaj . Mamy Duplicate i sklep w tym celu Xz U-polecenie. Następnie usuwamy wszystkie znaki znajdujące się w tym ciągu z danych wejściowych. Następnie dzielimy się na białe znaki za pomocą ð¡i usuwamy wszystkie puste ciągi (za pomocą ""K). Teraz to mamy .

To jest czysta wersja danych wejściowych, z którą będziemy pracować. Mapujemy każdy element za pomocą v. To służy yjako zmienna ciąg. Bierzemy pierwszy znak ciągu za pomocą ¬i push X, który zawiera ciąg ze wszystkimi zakazanymi znakami ( !"#$%&'()*+,-./:;<=>?@[\]^`{|}~). Sprawdzamy, czy lwielka wersja pierwszego znaku (który również zostanie ©zapisany w rejestrze), znajduje się w tym ciągu za pomocą å. Objęte tą częścią: ï>ijeśli pierwsza litera nie istnieje w ciągu zabronionych znaków ( X), dołączamy ten list do listy zabronionych znaków (wykonanych z X®«U) i przesuwamy yna stos.

Wreszcie, gdy łańcuchy są filtrowane, łączymy stos za pomocą spacji ðý.

Adnan
źródło
1
... wyjaśnienie? :-)
Luis Mendo
@LuisMendo Dzięki za przypomnienie! Gotowe :)
Adnan
2

PHP

Zainspirowany użyciem wyrażenia regularnego w większości odpowiedzi, początkowo próbowałem to zrobić bez użycia wyrażenia regularnego w celu pokazania zgrabnej wariacji, ale problem z brakiem czystych ciągów jako danych wejściowych zrujnował ten pomysł. Smutny.

Z opakowaniem funkcji, 89 bajtów

function f($s){foreach(preg_split('/\W/',$s)as$w)$c[lcfirst($w)[0]]++?:$v.=" $w";echo$v;}

Bez opakowania funkcji (wymagające uprzednio zadeklarowanego $ s), 73 bajty

foreach(preg_split('/\W/',$s)as$w)$c[lcfirst($w)[0]]++?:$v.=" $w";echo$v;

Wyjaśnienie:

foreach(preg_split('/\W/',$s)as$w)$c[lcfirst($w)[0]]++?:$v.=" $w";echo$v;
        preg_split('/\w/',$s)                                             Break input on all non-word characters
foreach(                     as$w)                                        Loop through each 'word'
                                     lcfirst($w)[0]                       Take the first letter of the lowercase version of the word
                                  $c[              ]++?:                  Increment an array element with a key of that letter after checking if it's false-y (0)
                                                        $v.=" $w";        Add the word if the letter wasn't found (if the previous condition evaluated to false)
                                                                  echo$v; Print the new string to screen.

Żałuję tylko, że nie mogłem znaleźć szybszego sposobu sprawdzenia / konwersji liter.

Xanderhall
źródło
2

Python, 103 bajty

import re
lambda s,d=[]:[w for w in re.findall("\w+",s)if(d.append(w.lower()[0])or d[-1])not in d[:-1]]
orlp
źródło
1

Lua, 172 bajtów

To skończyło się znacznie dłużej, niż chciałem

t={}(...):gsub("[%w_]+",function(w)b=nil for i=1,#t
do b=t[i]:sub(1,1):lower()==w:sub(1,1):lower()and 1 or b
end t[#t+1]=not b and w or nil end)print(table.concat(t," "))

Nie golfił

t={}                           -- initialise the accepted words list
(...):gsub("[%w_]+",function(w)-- iterate over each group of alphanumericals and underscores
  b=nil                        -- initialise b (boolean->do we have this letter or not)
  for i=1,#t                   -- iterate over t
  do
    b=t[i]:sub(1,1):lower()    -- compare the first char of t's i word
       ==w:sub(1,1):lower()    -- and the first char of the current word
           and 1               -- if they are equals, set b to 1
           or b                -- else, don't change it
  end
  t[#t+1]=not b and w or nil   -- insert w into t if b isn't set
end)

print(table.concat(t," "))     -- print the content of t separated by spaces
Katenkyo
źródło
1

Poważnie, 43 bajty

6╙¬▀'_+,;)-@s`;0@Eùk`M┬i;╗;lrZ`i@╜í=`M@░' j

Wypróbuj online!

Brak możliwości wyrażenia regularnego sprawił, że stało się to o wiele trudniejsze niż trzeba.

Wyjaśnienie:

6╙¬▀'_+,;)-@s`;0@Eùk`M┬i;╗;lrZ`i@╜í=`M@░' j
6╙¬▀                                         push digits in base 62 (uppercase and lowercase letters and numbers)
    '_+                                      prepend underscore
       ,;)                                   push two copies of input, move one to bottom of stack
          -                                  get all characters in input that are not letters, numbers, or underscores
           @s                                split input on all occurrences of non-word characters
             `;0@Eùk`M                       for each word: push the first letter (lowercased)
                      ┬i                     transpose and flatten (TOS is list of first letters, then list of words)
                        ;╗                   push a copy of the first letters list to register 0
                          ;lrZ               zip the list of first letters with their positions in the list
                              `i@╜í=`M       for each first letter: push 1 if that is the first time the letter has been encountered (first index of the letter matches its own index) else 0
                                      @░     filter words (take words where corresponding element in the previous list is truthy)
                                        ' j  join on spaces
Mego
źródło
1

Ruby 76 bajtów

s;f={};s.scan(/(([\w])[\w]*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=!p; h)}.compact.*' '

Lub z definicją metody 88 bajtów

def m s;f={};(s.scan(/((\w)\w*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=1; h)}-[p]).*' ';end

Nie golf i z testem jednostkowym:

def m_long(s)
  #found  - Hash with already found initials
  f={}
  #h=hit, i=initial, j=i[0].downcase
  s.scan(/(([\w\d])[\w\d]*)/).map{|h,i| 
    f[j=i.upcase] ? nil : (f[j] = true; h)
  }.compact.join(' ')
end
#true == !p
#~ def m(s)
  #~ f={};s.scan(/(([\w\d])[\w\d]*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=!p; h)}.compact.join' '
#~ end
def m s;f={};s.scan(/(([\w\d])[\w\d]*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=!p; h)}.compact.join' ';end

#~ s = "Ferulas flourish in gorgeous gardens."
#~ p s.split

require 'minitest/autorun'
class FirstLetterTest < Minitest::Test
  def test_1
    assert_equal("Ferulas in gorgeous",m("Ferulas flourish in gorgeous gardens."))
    assert_equal("Ferulas in gorgeous",m_long("Ferulas flourish in gorgeous gardens."))
  end
  def test_2
    assert_equal("Take all first words each letter is",m("Take all first words for each letter... this is a test"))
    assert_equal("Take all first words each letter is",m_long("Take all first words for each letter... this is a test"))
  end
  def test_3
    assert_equal("Look _ There are 3 dogs",m("Look ^_^ .... There are 3 little dogs :)"))
    assert_equal("Look _ There are 3 dogs",m_long("Look ^_^ .... There are 3 little dogs :)"))
  end
  def test_4
    assert_equal("maybe some day 1 plus 2 could result in 3",m("...maybe some day 1 plus 2 plus 20 could result in 3"))
    assert_equal("maybe some day 1 plus 2 could result in 3",m_long("...maybe some day 1 plus 2 plus 20 could result in 3"))
  end
end
knut
źródło
W Regex \wzawiera znaki numeryczne, więc [\w\d]można je zastąpić \w. Ponadto, jeśli nilwartości są w tablicy podczas wywoływania join' '(lub jeszcze lepiej, *' 'jest to skrót, którego można użyć, aby zapisać więcej bajtów), znikają, więc wywołanie do compactjest niepotrzebne.
Wartość tuszu
@KevinLau Thanks. To \w\ddla mnie krępujące. Ale jeśli usunę compact, dostanę dodatkowe spacje, (patrz ['x',nil,'x']*'y' == 'xyyx'). A może coś przeoczyłem?
knut
Ups, masz rację. W takim przypadku (list-[p])oszczędza bajty list.compact. Również /\w/jest równoważne z /[\w]/. Wreszcie, można wymienić nilz pa !pz 1(ponieważ tylko swoją hash potrzebuje wartości truthy w nim)
Value Ink
Dzięki, dodałem swoje uwagi, wymiana nilze pnie działa. Jeśli użyję go w kodzie, pojawia się błąd składniowy. Muszę enkapsulować jak (p)- ale potem znów mam 3 znaki.
knut
Klapki trójskładnikowej i to działa, aby zapisać bajt: !f[j=i.upcase]?(f[j]=1;h):p. Pomyślałem też o tym, ale z powodu indeksowania ciągów, używania s.scan(/\w+/)i usuwania również ina korzyść h[0]utworów.
Wartość tuszu
1

grep i awk, 68 56 bajtów

Scenariusz:

echo `grep -o '\w*'|awk '!x[tolower(substr($0,1,1))]++'`

Wyjaśnienie:

  • grep -o dopasowuje słowa prawne, drukując każde w osobnej linii.

  • awkpobiera pierwszą literę każdego wiersza substr, zamienia ją na małe litery, a następnie inkrementuje wpis hashtable tym kluczem. Jeśli wartość została cofnięta przed przyrostem, linia jest drukowana.

  • echo ... zamienia wiersze z powrotem w słowa

I wcześniej starał się stworzyć rozwiązanie bez awk, za pomocą uniq, sort, grepa bashjednak padł właśnie krótki. Historia w edycjach.

Dzięki Dennis za ulepszenia, za którymi tęskniłem.

joeytwiddle
źródło
0

Python 3.5, 138 bajtów:

import re;lambda o,t=[]:''.join([y[0]for y in[(u+' ',t.append(u[0].lower()))for u in re.sub('\W+',' ',o).split()if u[0].lower()not in t]])

Zasadniczo dzieje się…

  1. Używając prostego wyrażenia regularnego, program zastępuje wszystkie znaki, z wyjątkiem małych i wielkich liter, cyfr lub znaków podkreślenia w danym ciągu, spacjami, a następnie dzieli ciąg w tych spacjach.
  2. Następnie, korzystając ze zrozumienia listy, utwórz listę, która będzie się powtarzać przez wszystkie słowa w podzielonym ciągu i dodaj pierwsze litery każdego słowa do listy „t”.
  3. W tym przypadku, jeśli pierwsza litera bieżącego słowa NIE znajduje się już na liście „t”, to słowo i końcowe spacje są dodawane do tworzonej listy bieżącej. W przeciwnym razie lista kontynuuje dodawanie pierwszych liter każdego słowa do listy „t”.
  4. Na koniec, po przejściu wszystkich słów w podziale, słowa z nowej listy są łączone w ciąg i zwracane.
R. Kap
źródło
0

PHP 120 bajtów

function a($s){foreach(preg_split('/\W/',$s)as$w)if(!$o[ucfirst($w[0])]){$o[ucfirst($w[0])]=$w;}return implode(" ",$o);}

To generuje wiele ostrzeżeń, ale to w porządku.

użytkownik52869
źródło
Czy to functionkonieczne?
AL
0

JavaScript ES6, 108 107 znaków

107 znaków, ciąg wyników jest przycięty

r=s=>s.split``.reverse().join``
f=s=>r(r(s).replace(/\b\w*(\w)\b(?=.*\1\b)/gi,'')).replace(/\W+/g,' ').trim()

Test:

["Take all first words for each letter... this is a test",
"Look ^_^ .... There are 3 little dogs :)",
"...maybe some day 1 plus 2 plus 20 could result in 3"
].map(f) + '' == [
"Take all first words each letter is",
"Look _ There are 3 dogs",
"maybe some day 1 plus 2 could result in 3"
]
Qwertiy
źródło
f= się nie liczy .
Qwertiy
0

Tcl , 150 bajtów

proc F {s D\ {}} {lmap w [split $s] {regsub -all \[^\\w] $w "" f
if {![dict e $D [set k [string tol [string in $f 0]]]]} {dict se D $k $f}}
dict v $D}

Wypróbuj online!

sergiol
źródło