Classifique os meses do ano

19

Escreva uma função ou programa que leva entradas de string, totalmente escrito, os nomes dos meses Inglês em caso de título: January, February, March, etc. (null / CR / LF encerrado OK, delimitado com alguns caracteres não-alfa se assim preferir) e quer

  • compara duas entradas, retornando um valor Truthy se a segunda entrada for maior (na ordem do mês) que a primeira. Valores iguais resultam em um valor de Falsey

  • ou classifica uma sequência arbitrária (lista, sequência delimitada etc.) em ordem cronológica

(O ponto crucial do desafio é definir um método / expressão que dê a classificação lexicográfica correta. Alguns idiomas podem ter uma resposta mais curta com um ou outro)

Você não pode usar nenhum método interno de análise de tempo (por exemplo strptime) para converter o nome do mês em um número ou em um mapeamento pré-definido de nomes de meses. Use as propriedades das próprias strings, uma tabela de consulta parcimoniosa que você definir ou algo inteligente.

Exemplo

Exemplos de funcionamento, embora o primeiro seja proibido pelas regras ...

import datetime
def is_later_month(a, b):
    '''
    Example of prohibited code because it relies on language 
    features about how to parse month names
    '''
    return datetime.strptime(a, '%B') < datetime.strptime(b, '%B') 

As versões abaixo estão OK, porque codificamos essas informações

months = {
    'January':  1, 'February':  2, 'March':      3,
    'April':    4, 'May':       5, 'June':       6,
    'July':     7, 'August':    8, 'September':  9,
    'October': 10, 'November': 11, 'December':  12,
}
def is_later_month(a, b):
    """
    Returns True/False when comparing two months.
    """
    return months[a] < months[b]

Ou você pode fazer uma função de classificação

months = {'as above...'}
def sort_months(l):
    """
    Sorts list and returns it. Different input and output than the above, 
    but equally valid. Sorting versus comparing might be shorter in your
    favorite language.
    """
    return sorted(l, key=lambda x: months[x]) 

Testes de exemplo

assert is_later_month('January', 'February')
assert is_later_month('January', 'December')
assert is_later_month('November', 'December')
assert not is_later_month('July', 'July')
assert not is_later_month('October', 'September')
Nick T
fonte
Você não pode usar nenhum método interno de análise de tempo (por exemplo, strptime) para converter o nome do mês em um número. Isso é um pouco claro. Podemos usar o literal predefinido de uma linguagem que contém os nomes dos meses?
Luis Mendo
Vou excluir minha resposta então. Mas ainda não está claro o que é permitido e o que não é.
Luis Mendo
O problema é que você não pode prever todos esses truques em potencial, como matrizes predefinidas. Talvez uma opção melhor tivesse sido usar um conjunto menos comum de strings, como nomes inventados. Mas agora é tarde demais para isso, eu acho
Luis Mendo
O que estou expressando é claro? Se o Python tivesse um built-in monthsque fosse uma lista de todos os nomes de meses, eu gostaria de proibir months[x] < months[y]como resposta. A lista de nomes de meses possui alguns recursos mais peculiares (duração variável, semelhança) que facilitam / dificultam o desafio em relação a cadeias geradas aleatoriamente.
Nick T
Sim, acho claro. Eu só temo que pode haver outros casos semelhantes que não foram explicitamente excluídos (mas eu não sei quais)
Luis Mendo

Respostas:

41

Geléia , 19 bytes

11ị“bMAanlseovc”iµÞ

Este é um link monádico que pega uma lista como argumento e a classifica. Experimente online!

fundo

O Jelly usa indexação modular baseada em 1. Se repetirmos os nomes dos meses com frequência suficiente para obter 11 caracteres, obteremos a seguinte matriz.

J a n u a r y J a n u
F e b r u a r y F e b
M a r c h M a r c h M
A p r i l A p r i l A
M a y M a y M a y M a
J u n e J u n e J u n
J u l y J u l y J u l
A u g u s t A u g u s
S e p t e m b e r S e
O c t o b e r O c t o
N o v e m b e r N o v
D e c e m b e r D e c

Na 11 ª coluna (última), todos os personagens são diferentes, então nós podemos usá-los para identificar a ordem dos meses.

Como funciona

11ị“bMAanlseovc”iµÞ  Monadic link. Argument: A (array of months)

                 µ   Combine the preceding chain into a link.
                  Þ  Sort A by that link.
11ị                    Select the 11th character of the month's name.
   “bMAanlseovc”       Find the index of that character in "bMAanlseovc".
                       For 'u' ("January"), this returns 0 (not found).
Dennis
fonte
11
Apenas curioso, como você está classificando o mês com "bMAanlseovc"? Índice da correspondência do primeiro caractere?
Ljeabmreosn
Eu adicionei uma explicação.
Dennis
8
Uau, isso é realmente inteligente!
Ljeabmreosn
15

código de máquina x86, 26 25 bytes

Hexdump:

ff 32 8b 01 34 c0 68 30 5f 43 01 59 f7 e1 91 5a
80 f2 c0 f7 e2 3b c8 d6 c3

Código de montagem:

    push dword ptr [edx];
    mov eax, [ecx];
    xor al, 0xc0;
    push 0x01435f30;
    pop ecx;
    mul ecx;
    xchg eax, ecx;
    pop edx;
    xor dl, 0xc0;
    mul edx;
    cmp ecx, eax;
    _emit 0xd6;
    ret;

A seguinte função hash coloca os nomes dos meses na ordem correta (encontrada pela força bruta):

(x ^ 0xc0) * 0x01435f30

É aplicado aos primeiros 4 bytes (32 bits) da sequência de entrada, organizados em ordem little-endian. Em seguida, comparando o resultado e usando SALCpara definir o registro de resultados (al):

  • -1 (verdadeiro) se os meses estiverem em ordem
  • 0 (falso) se o segundo mês preceder o primeiro mês (ou forem iguais)
anatolyg
fonte
4
Estou impressionado. Um pedaço muito curto de código sem o uso de linguagem específica do código-golfe.
ShuberFu 11/08/19
13

Gelatina , 15 bytes

Oḅ32 354*%991µÞ

Nenhum link de intérprete online aqui, porque este é um envio lento . O programa usa a função de hash 354^(input interpreted as base 32 int) % 991como a chave de classificação, que fornece resultados na ordem correta. O programa não terminará tão cedo porque os resultados da exponenciação são gigantescos - para "setembro", um número com 0,24 quatrilhão de dígitos precisa ser calculado!

Explicação da geléia:

              Þ         Sort by...
             µ          Monadic link consisting of...

O                       Convert month string to code points
 ḅ32                    Take base 32
     354*               Perform 354 to the power of the result
         %991           Take modulo 991

Script de prova de conceito do Python - observe o uso de powpara exponenciação modular, que é muito mais eficiente:

import random

def base_convert(string, base):
    total = 0

    for c in string:
        total = total * base + ord(c)

    return total

def month_hash(month):
    return pow(354, base_convert(month, 32), 991)

months = ["January", "February", "March", "April", "May", "June", "July",
    "August", "September", "October", "November", "December"]
random.shuffle(months)

print(months)
print(sorted(months, key=month_hash))
Sp3000
fonte
5
"Nenhum link de intérprete online aqui, porque este é um envio lento ." Nesse caso, você também pode ordenar os meses manualmente. ;-)
owacoder
Talvez você poderia PR um pedido de recurso para otimizar pow / mod ...
Nick T
@NickT Essa é uma excelente ideia, mas infelizmente com a maneira como o intérprete é configurado (com cada operador definido separadamente), isso pode ser um pouco complicado. E Jelly não funciona bem com os operadores que têm mais de dois argumentos, então a definição de um operador independente não muito quer trabalhar ...
SP3000
Não é um operador separado nem nada, apenas uma introspecção mais profunda para ver se uma operação de energia é seguida por divisão modular. Parece fácil? : P
Nick T
5

Python, 64 61 57 bytes

lambda x,y,g='bMAanlseovc'.find:g((x*4)[10])<g((y*4)[10])

O lambda leva dois meses como entrada e os compara. Teste em Ideone .

Graças a @ljeabmreosn por jogar fora 3 bytes e abrir caminho para mais 3!

Dennis
fonte
2
Por fim, você revela o segredo por trás da magia negra usada para calcular rapidamente o mês correto na sua resposta de geléia!
Value Ink
11
Mudaria s[10%len(s)]para o (4*s)[10]trabalho?
Ljeabmreosn
11
@ljeabmreosn Isso funciona mesmo. Obrigado!
Dennis
11
Não vi o <strike> ab </ strike> uso de argumentos padrão em um lambda ainda: P
Nick T
4

Python, 81 71 bytes

lambda x,y,m='anebarprayunulugepctovec':m.index(x[1:3])<m.index(y[1:3])

https://repl.it/CluN/1

Compara o índice mda segunda e terceira letras de dois meses.

Versão de 83 bytes para classificar uma lista de meses:

lambda x:sorted(x,key=lambda i:'JanFebMarAprMayJunJulAugSepOctNovDec'.index(i[:3]))
atlasologist
fonte
3

Ruby, 58 bytes

Usa o truque de classificação do mês da resposta do @ atlasologist .

->a{a.sort_by{|i|"anebarprayunulugepctovec".index i[1,2]}}

A função de comparação é um pouco mais longa, com 63 bytes

->a,b{m=->i{"anebarprayunulugepctovec".index i[1,2]};m[a]<m[b]}
Value Ink
fonte
3

J, 66 65 bytes

Usa o fato de que f (m) = 2 * (ord (m [0]) + ord (m [-1])) // len (m) é uma função válida no domínio limitado dos 12 meses:

>/@:('7/HEäWa<+0'&i.@(3 :'u:<.(#>y)%~+:+/3&u:({.>y),({:>y)')"0)"1

Uso:

   bigger =: >/@:('7/HEäWa<+0'&i.@(3 :'u:<.(#>y)%~+:+/3&u:({.>y),({:>y)')"0)"1
   bigger ('May'; 'March')
1
   bigger ('May'; 'June')
0

(De maneira alguma essa é a melhor idéia, mas eu não queria roubar o truque de ninguém!)

Aqui está uma versão mais curta usando o método @ atlasologist :

J, 63 bytes

m=:[:}.3{.]
[:>/(12 2$'anebarprayunulugepctovec')i.([:m[),:[:m]

Uso:

   bigger =: [:>/(12 2$'anebarprayunulugepctovec')i.([:m[),:[:m]
   'January' bigger 'May'
0
   'June' bigger 'May'
1

E uma versão muito mais curta usando o método inteligente de @ Dennis :

J, 34 bytes

>&:('ubMAanlseov'&i.@:{.@:(10&|.))
ljeabmreosn
fonte
3

Haskell, 74 bytes

Meu primeiro código de golfe, yay! A idéia geral deste é inspirada na resposta principal em Jelly, e no fato de que, quando os nomes dos meses são alternados, o décimo primeiro caractere é sempre único.

e s=head.drop 10$cycle s;a#b=elem(e b)$tail$dropWhile(/=e a)"ubMAanlseovc"

Aqui está uma versão não destruída para ver como funciona:

order :: String
order = "ubMAanlseovc"

eleventhChar :: String -> Char
eleventhChar
  = head . drop 10 $ cycle

inOrder :: String -> String -> Bool
inOrder m1 m2
  = elem (eleventhChar m2) (tail $ dropWhile (/= eleventhChar m1) order)

A efunção representa a função décimo primeiro char (infelizmente não é possível retirar 4 bytes devido à restrição de monomorfismo, eu acho) e a #função infix corresponde à inOrderfunção.

Uma solução pequena e elegante, mas pode haver maneiras de eliminar mais bytes (eu encontrei alguns enquanto escrevia isso!)

caramanchão
fonte
Você pode encurtar e s=head.drop 10$cycle scomo você fez na sua explicação usando .em vez de $: e=head.drop 10.cycle. No entanto usando o operador de índice de lista !!é ainda menor:e=(!!10).cycle
Laikoni
Ótimas sugestões. Às vezes você simplesmente ignora essas coisas. Muito obrigado. Irá editar em breve.
pavilhão
2

Java, 133 123

Golfe:

boolean f(String a,String b){return h(a)<h(b);}int h(String s){return"anebarprayunulugepctovec".indexOf(s.substring(1,3));}

Eu estava procurando por uma técnica inteligente, como na resposta do montador, mas estava demorando muito para descobrir, então eu segui a mesma técnica que todos os outros usavam.

Ungolfed:

import java.util.Random;

public class SortTheMonthsOfTheYear {

  public static void main(String[] args) {
    // @formatter:off
    String[] MONTHS = new String[] {
        "January", "February", "March",
        "April",   "May",      "June",
        "July",    "August",   "September",
        "October", "November", "December"
    };
    // @formatter:on

    Random r = new Random();
    for (int i = 0; i < 100; ++i) {
      int m1 = r.nextInt(MONTHS.length);
      int m2 = r.nextInt(MONTHS.length);
      System.out.println("Input: " + MONTHS[m1] + " < " + MONTHS[m2]);
      System.out.println("Expected: " + (m1 < m2));
      System.out.println("Actual:   " + new SortTheMonthsOfTheYear().f(MONTHS[m1], MONTHS[m2]));
      System.out.println();
    }
  }

  // Begin golf
  boolean f(String a, String b) {
    return h(a) < h(b);
  }

  int h(String s) {
    return "anebarprayunulugepctovec".indexOf(s.substring(1, 3));
  }
  // End golf

}

fonte
Você poderia usar substringem vez secharAt
anatolyg
@ anatolyg obrigado, não sei como isso me escapou. Também fui capaz de remover, "" +pois não existem mais dados brutos char.
2

Linguagem de máquina ARM no Linux 44 40 bytes

e28fc001     add ip, pc, #1
e12fff1c     bx ip
6803         ldr r3, [r0, #0]
6808         ldr r0, [r1, #0]
4a05         ldr r2, [pc, #20]
f08303dd     eor.w r3, r3, #221
f08000dd     eor.w r0, r0, #221
4353         muls r3, r2
4350         muls r0, r2
4283         cmp r3, r0
bfac         ite ge
2000         movge r0, #0
2001         movlt r0, #1
4770         bx lr
2f68f24c

Eu usei uma função hash diferente do que anatolyg da solução e tentou instruções uso polegar para salvar alguns bytes (embora soprou 8 bytes que entram modo polegar).

Você pode fazer isso em um dispositivo Raspberry Pi ou Android com GNURoot.

int main(int argc,char**argv){
return ((int(*)(char*,char*))"\
\1\xc0\x8f\xe2\
\x1c\xff\x2f\xe1\
\3\x68\x8\x68\
\5\x4a\x83\xf0\
\xdd\3\x80\xf0\
\xdd\x43\x53\x43\
\x50\x4a\x83\x42\
\xac\bf\0\x20\
\1\x20\x70\x47\
\x4c\xf2\x68\x2f\
")(argv[1],argv[2]);}

Para executar, digite algo como

$ ./foo January February; echo $?

A versão atual agora lida com o caso de igualdade (e outros) corretamente.

teto
fonte
Eu acho que você não precisa de código que mude explicitamente para o modo Thumb. Pelo que me lembro, você só precisa informar ao vinculador que seu procedimento está no modo miniatura, e o vinculador definirá o LSB no endereço do procedimento como 1, para que o processador alterne para o modo Thumb automaticamente quando seu código for chamado.
precisa saber é
Além disso, o que bfacfaz?
precisa saber é
@anatolyg ite geexecuta condicionalmente a próxima instrução ( movge r0, #0) se r3 >= r0, caso contrário, a instrução a seguir é executada ( movlt r0, #1). Eu acho que há espaço para derrubar um par de bytes aqui, mas eu não tive tempo para trabalhar neste :-)
ceilingcat
1

Perl 6 , 55 bytes

*.sort({index 'anebarprayunulugepctovec',.substr(1,2)})

Seria necessário mais alguns bytes para as versões de comparação:

{[<] @_».&{index 'anebarprayunulugepctovec',.substr(1,2)}}
{[<] .map: {index 'anebarprayunulugepctovec',.substr(1,2)}}

Teste:

#! /usr/bin/env perl6
use v6.c;
use Test;

my @months = <
  January February March April May June July
  August September October November December
>;

my &month-sort = *.sort({index 'anebarprayunulugepctovec',.substr(1,2)});

plan 100;

for ^100 {
  # 「.pick(*)」 returns all elements in random order
  is-deeply month-sort(@months.pick(*)), @months.List;
}
Brad Gilbert b2gills
fonte
1

Haskell, 118 caracteres

data M=Ju|Fr|Mc|Ai|My|Je|Jy|Au|St|Oo|Ne|De deriving(Ord,Eq,Read)
r=read.([head,last]<*>).lines.take 4
a#b=(r a::M)<r b

Usa o fato de que o nome de cada mês é único em seu primeiro e quarto caracteres (ou em 3 de maio) para definir um tipo de dados que pode ser analisado e comparado automaticamente pelo idioma. A função 'r' converte uma string pegando os quatro primeiros caracteres (ou menos) e depois escolhendo o primeiro e o último. Então 'a # b' é um operador para comparar os valores:

*Main> "June" # "March"
False
*Main> "June" # "July"
True
*Main> "January" # "July"
True
*Main> "January" # "November"
True
*Main> "December" # "November"
False

Provavelmente poderia ser feito de maneira mais eficiente, mas eu queria tentar fazer isso usando um tipo de dados útil para representar os meses.

Jules
fonte
1

PowerShell, 96 88 63 bytes

$input|Sort{'anebarprayunulugepctovec'.IndexOf((-join$_[1,2]))}

por exemplo

PS C:\Code> 'February', 'January', 'December', 'April' | .\monthsort.ps1
January
February
April
December

Agora, o segundo desafio é classificar uma lista em ordem; versões anteriores fizeram a comparação do teste de dois meses:

v2.
$l,$r=$args|%{-join$_[1,2]};($d='anebarprayunulugepctovec').indexof($l)-lt$d.IndexOf($r)

v1.
$l,$r=$args|%{-join$_[1,2]};$r-match('an|eb|ar|pr|ay|un|ul|ug|ep|ct|ov|ec'-split$l)[1].Trim('|')

e.g.

PS C:\code> .\Test-MonthsInOrder.ps1 January February
True

Com base nos dois segundos caracteres no nome do mês.

TessellatingHeckler
fonte
1

Python 83 82 bytes

lambda x,y,m=(lambda a:'2&9<@FD6A?L%'.find(chr(sum(map(ord,a[:3]))%77))):m(x)<m(y)

Teste: https://repl.it/repls/TimelyDecimalBrowsers

Obtém a soma dos 3 primeiros caracteres e cria um único caractere para pesquisar.

Baris
fonte
0

Javascript, 118 bytes

u=p=>{p=p.split` `.sort();c=[];for(i=0;i<12;i++){c.push(p["4 3 7 0 8 6 5 1 11 10 9 2".split` `[i]]);}return c.join` `}

Poderia ser mais jogado, provavelmente se livrando ce usando array.map, mas é isso que tenho por enquanto ...

Bald Bantha
fonte
for(i=0;i<12;)c.push(p[[4,3,7,0,8,6,5,1,11,10,9,2][i++]]);
pinkfloydx33
0

Bash, 101 bytes

isso é função como is_later

f(){ s=ubMAanlseovc a=$1$1$1 b=$2$2$2 c=${a:10:1} d=${b:10:1} e=${s%$c*} f=${s%$d*};((${#e}<${#f}));}

teste

$ f January December && echo later || echo not later
not later
user58494
fonte
0

k4, 29

{x@<"ubMAanlseovc"?(*|11#)'x}

Um porto da resposta da @ Dennis's Jelly .

Este é o classificador, não o comparador; Curiosamente, o comparador é trivialmente implementável pelo mesmo algoritmo e apenas um byte a mais:

{(<)."ubMAanlseovc"?(*|11#)'x}
Aaron Davies
fonte
0

Bash + coreutils, 94 bytes 93 bytes

s(){ x=FMAyulgSOND;for y;{ rev<<<${y:0:3}|tr -d -C $x|tr $x C-M;echo B$y;}|sort|cut -f2 -dB;}

Esta é uma tentativa de criar uma transformação que classifique lexicograficamente. Se você olhar atentamente para a chave de transformaçãoFMAyulgSOND poderá ver os meses de fevereiro a dezembro (janeiro fica vazio após a transformação; ela é puxada para cima usando 'B' como separador). A reversão, truncamento e remoção de letras sem chave permitem que esse truque seja realizado.

90 bytes usando localidade C

s(){ x=FMAyulgSOND;for y;{ rev<<<${y:0:3}|tr -d -C $x|tr $x C-M;echo \␉$y;}|sort|cut -f2;}

... onde ␉ é o caractere de tabulação.

80 bytes usando localidade C

s(){ x=anebarprayunulugepctovec;for y;{ echo ${x%${y:1:2}*}\␉$y;}|sort|cut -f2;}

... usando o método @ atlasolog. Limite para ser uma maneira de usar essa abordagem para trabalhar com mais localidades.

Teste / Uso

s December November October September August July June May April March February January

saídas:

January
February
March
April
May
June
July
August
September
October
November
December
H Walters
fonte