Golf-me um OOP!

26

Golf-me um OOP!

Dois componentes importantes da programação orientada a objetos são herança e composição. Juntos, eles permitem criar hierarquias de classe simples e poderosas para resolver problemas. Sua tarefa é analisar uma série de instruções sobre uma hierarquia de classes e responder a perguntas sobre a hierarquia.

Entrada

Uma série de declarações e perguntas sobre uma hierarquia de classes, lida em um arquivo ou entrada padrão, o que for melhor para o seu idioma. Se você usar a opção de arquivo, o nome do arquivo será passado como o primeiro argumento para o seu código (argumento da função ou argumento da linha de comando, o que você escolher). O formato é o seguinte:

<statement> : <name> is a <name>. | <name> has a <name>.
<question> : Is <name> a <name>? | Does <name> have a <name>?
<name> : a-z | A-Z | sequence of alphanumerics or underscores, starting with a letter

A entrada será sempre declarações, depois perguntas. Todos os nomes de turma começarão com uma letra maiúscula em inglês ( A-Z) e todos os nomes de membros começarão com uma letra minúscula em inglês ( a-z). Todos os nomes diferenciam maiúsculas de minúsculas - ABC123não são da mesma classe que Abc123.

Não haverá herança cíclica - se Bherdar de A, Anão herdará de Bou qualquer um dos Bfilhos.

Somente nomes de classe farão parte de uma hierarquia - instruções como foo is a bar.ou document has a name.não ocorrerão.

Saída

Uma série de valores verdadeiros ou falsos, como respostas às consultas, gravadas na saída padrão ou como o valor de retorno da sua função. Se você não tiver informações suficientes para responder a uma pergunta (por exemplo, perguntas que envolvam nomes que você não viu nas declarações), responda com um valor falsey.

Casos de teste

Caso 1:

Entrada:

B is a A.
C is a B.
A has a foo.
Does B have a foo?
Is C a A?
Is D a A?

Saída:

True
True
False

Caso 2:

Entrada:

Cop is a Person.
Criminal is a Person.
Sheriff is a Cop.
Crooked_Cop is a Cop.
Crooked_Cop is a Criminal.
BankRobber is a Criminal.
Cop has a badge.
Criminal has a criminal_record.
Person has a name.
Is Crooked_Cop a Person?
Does Criminal have a name?
Is Crooked_Cop a BankRobber?
Does Person have a potato?
Is Cop a Cop?

Saída:

True
True
False
False
True

Regras

  • Você pode responder com uma função ou um programa
  • As brechas padrão são proibidas
  • Isso é , então a resposta correta mais curta em bytes ganha
  • A resposta vencedora será escolhida em uma semana

Boa sorte, e que o OOP esteja com você!

Entre os melhores

O snippet de pilha na parte inferior desta postagem gera o cabeçalho das respostas a) como uma lista da solução mais curta por idioma eb) como um cabeçalho geral.

Para garantir que sua resposta seja exibida, inicie-a com um título, usando o seguinte modelo de remarcação:

## Language Name, N bytes

onde Nestá o tamanho do seu envio. Se você melhorar sua pontuação, poderá manter as pontuações antigas no título, identificando-as. Por exemplo:

## Ruby, <s>104</s> <s>101</s> 96 bytes

Se você quiser incluir vários números no cabeçalho (por exemplo, porque sua pontuação é a soma de dois arquivos ou você deseja listar as penalidades do sinalizador de intérpretes separadamente), verifique se a pontuação real é o último número no cabeçalho:

## Perl, 43 + 2 (-p flag) = 45 bytes

Você também pode transformar o nome do idioma em um link que será exibido no snippet:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

Mego
fonte
Como é Does Criminal have a name?igual a True? Todos os objetos têm um nome?
precisa
4
@JAtkin Criminal is a Person. Person has a name.
Reto Koradi
Ahh ... eu tinha sentido falta disso.
precisa
Preciso receber toda a entrada de uma só vez ou posso levá-la linha a linha como um console interativo? Se # 2, posso emitir uma verdade \ falsey mesmo se a entrada for uma declaração?
J Atkins
@JAtkin Tudo de uma vez ou linha por linha, sua escolha. Se for uma declaração, não deve haver saída. Somente perguntas obtêm respostas.
Mego

Respostas:

13

CJam, 59 bytes

q_'.e=\N/{)'?=\S/>_,(%}%/(__,*{(2$z~@f=.*\m*|}/ff{1$e=>:=N}

Isso termina instantaneamente nos dois casos de teste.

Ou imprime o segundo nome da pergunta ou 1(ambos de verdade) ou 0(falso).

Experimente on-line no intérprete CJam .

Idéia

Devido à distinção entre classes e membros, o desafio se resume a criar uma pré - encomenda para a qual a entrada fornece uma definição parcial.

Definimos que xy se x é um y ou x tem um y .

Para o primeiro caso de teste, a entrada indica que BA , CB e Afoo . Por causa da transitividade, também temos Bfoo , CA e Afoo . Além disso, devido à reflexividade, xx é sempre verdade.

Para uma determinada entrada, podemos extrair a definição parcial de ≺ das declarações, aplicar transitividade por um número suficiente de vezes para concluir a definição de ≺ e finalmente responder às perguntas.

Código

q_     e# Push all input from STDIN and a copy.
'.e=   e# Count the number of dots/statements (C).
\N/    e# Split the original input at linefeeds.
{      e# For each line:
  )'?= e#   Pop the last character and check if it is a question mark.
       e#   Pushes 1 for '?', 0 for '.'.
  \S/  e#   Split the modified line at spaces.
  >    e#   Remove the first chunk ("Does" or "Is") for questions.
  _,(% e#   Keep the first and last element of the resulting array.
}%/    e# Split the line array into chunks of length C.
(_     e# Extract the first chunk (statements) and push a copy.
       e# The original becomes an accumulator for ≺.
_,*    e# Repeat the statements C times.
{      e# For each of the repeated statements:
  (    e#   Shift out the first name.
       e#     ["w" "x"] -> ["x"] "w"
  2$z~ e#   Copy the accumulator, zip it and dump.
       e#     [["x" "y"] ["z" "w"]] -> ["x" "z"] ["y" "w"]
  @f=  e#   Rotate the shifted out name on top and check for equality.
       e#     ["y" "w"] "w" -> [0 1]
  .*   e#   Vectorized string repetition.
       e#     ["x" "z"] [0 1] -> ["" "z"]
  \m*  e#   Swap the result with the shifted array and apply Cartesian product.
       e#     ["" "z"] ["x"] -> [["" "x"] ["z" "x"]]
       e#   This accounts for transitivity; we had ["w" "x"] and ["z" "w"],
       e#   so now we have ["z" "x"].
  |    e#   Perform set union with the accumulator to add the new pairs.
}/     e#
ff{    e# For each of the questions on the bottom of the stack.
  1$e= e#   Count the occurrences of the question pair in the accumulator.
  >    e#   Remove 0 or 1 elements from the question pair.
  :=   e#   Check for equality.
       e#   If the question pair occurs in the accumulator, this pushes the
       e#   second name of the question pair. Otherwise, it pushes 1 if the
       e#   names are equal (to account for reflexivity) and 0 otherwise.
  N    e#   Push a linefeed.
}      e#
Dennis
fonte
2
Isso é impressionante, considerando que o CJam não tem classes: D
Beta Decay
Isso é lindo.
Mego
As classes @BetaDecay são essencialmente conjuntos aninhados; aulas são implementadas em todos os idiomas. Diga no primeiro exemplo. C:{B:{A:{foo:{}}}}
Um
8

Python 3, 431 331 308 bytes

o={}
f={}
def h(z,f):
 if z not in o:f[z]=[z];o[z]=[]
while 1:
 g=input().split(' ');r=2;l=g[-1][:-1]
 if'.'in g[3]:
  if'i'in g[1]:h(g[0],f);h(l,f);f[g[0]]+=f[l]
  if'h'in g[1]:o[g[0]]+=l,
 else:
  if'I'in g[0]:r=any(l in z for z in f[g[1]])
  if'D'in g[0]:r=any(l in o[z] for z in f[g[1]])
 if r<2:print(r)

Esta é a versão completa com comentários

objects = {}
synonyms = {}

def createObject(name):
    """
    Create a object with `name` if is does not yet exist and start a synonym tree.
    """
    if name not in objects:
        synonyms[name] = [name]
        objects[name] = []

# use this to read from a file
# with open("questions.txt") as file: 
#     for l in file:
        # print(">>> " + l, end='')
        # inArg = l.replace("\n","").split(" ")


while True: # to read from a file comment this
        inArg = input(">>> ").split(" ") # and this out

        out = -1

        if '.' in inArg[3]: # statement
            last = inArg[3].replace('.','')

            if 'i' in inArg[1]: # is a
                createObject(inArg[0])
                createObject(last)
                synonyms[inArg[0]] += synonyms[last]

            if 'h' in inArg[1]: # has a
                objects[inArg[0]] += [last]

        else:# question
            last = inArg[-1].replace('?','')

            createObject(inArg[1])
            if 'I'in inArg[0]: # Is a
                out = any([last in syn for syn in synonyms[inArg[1]]])

            if 'D'in inArg[0]: # Does have a
                out = any(last in objects[syn] for syn in synonyms[inArg[1]])

        if out != -1:
            print(out)

Saída para o caso de teste nº 1:

True
True
False

Caso 2:

True
True
False
False
True

Eu removi os comandos debug para maior clareza no programa principal, mas se você gostaria de vê-los, basta olhar no histórico

J Atkin
fonte
Em vez de usar global fem h(z), uso def h(z,f)e passar o mundial fem quando chamá-lo. Na verdade, você não precisa de h(z)nada - basta colocar o corpo onde você o chama. Você não precisa r=2, e pode simplesmente ficar print(r)sem o if, pois precisa gerar um valor falsey para consultas falsas. Você pode renomear syna ze raspar vários bytes lá. Não acho que você precise da []compreensão da sua lista no começo any.
Mego
Você também usa euma vez, para poder eliminar a definição e apenas usá-la [a,b,c,d]. Em vez de if s(i,g) is not None, objetos do if s(i,g)- re.Matchsão sempre avaliados Truese uma correspondência for encontrada. Você também pode soltar 2 bytes com f[x]+=f[y].
Mego
@Mego Uau, obrigado por todas as dicas. Vou precisar colocá-los mais tarde.
precisa
Este post provavelmente irá ajudá-lo muito
Mego
@Mego Muito obrigado, agora é 396. Vou postar em breve.
J Atkins
4

Haskell, 157 bytes

o s=v(x 0#k)#(x 1#q)where(k,q)=break((=='?').l.l)(words#lines s)
x n w=(w!!n,init$l w)
v k(a,c)=a==c||or[v k(b,c)|b<-snd#(filter((==a).fst)k)]
(#)=map
l=last

Dê a string para o. Não tenho certeza se criar xe v('extrair' e 'verificar') infixes é mais do que criar mapum infixo ou se ambos são possíveis.

EDIT: Explicação

Então, (#)é assim que você define um operador infix, eu o uso apenas como uma abreviação de map, aplicando uma função a cada elemento de uma lista. Resolvendo esse e o outro alias l, evitando o operador 'aplicativo de função direta' $e adicionando ainda mais parênteses e espaçando as coisas, e com nomes de funções reais, chegamos a:

oop string = map (verify (map (extract 0) knowledge)) (map (extract 1) questions)
 where (knowledge,questions) = break ((=='?').last.last) (map words (lines string))

extract n wordlist = (wordlist!!n,init (last wordlist))

verify knowledge (a,c) = (a==c)
               || or [verify knowledge (b,c) | b <- map snd (filter ((==a).fst) knowledge)]

map words (lines string) é uma lista de listas de palavras de cada linha na sequência de entrada.

(=='?').last.last é um predicado indicando se a última letra da última palavra de uma linha é um ponto de interrogação, ou seja, se a linha é uma pergunta.

break quebra a lista em uma tupla da primeira parte sem perguntas (todas as declarações) e a parte da primeira pergunta em (todas as perguntas).

mappingar extract nisso tira de cada palavra a lista dos elementos que realmente queremos, o nth (que em declarações é a primeira palavra - e n == 0, em perguntas, a segunda palavra - so n == 1) usando o !!operador e o último, a partir do qual nós tem que cortar a última letra ( '.'ou '?') usando init.

(Observe que eu ignoro completamente a capitalização, é porque ignoro completamente a distinção entre classes e membros, os membros são apenas folhas de uma árvore construída pela base de conhecimento (mas nem todas as folhas representam membros, elas também podem ser classes sem subclasses nem membros ), na qual cada nó filho representa uma subclasse ou membro do que o nó pai representa. Eu percebi que isso é uma coisa errada a fazer nos casos não cobertos pelo OP. Editará a solução em breve.)

Agora, map (extract 0) knowledgee map (extract 1) questionssão listas de tuplas de nomes que representam um relacionamento de subclasse ou membro do primeiro ao segundo.

As tuplas em map (extract 0) knowledgesão todos os relacionamentos verdadeiros, aqueles em map (extract 1) questionsagora são mapeados para a verifyfunção, com o primeiro argumento definido como map (extract 0) knowledge.

(A partir de agora, dentro verify, knowledgeé um nome de parâmetro e se refere à extractlista de tuplas já editada.)

(Além disso, ao ler verify, observe que, embora o ||(após a quebra de linha deselegante para evitar rolagem horizontal no SE) seja uma disjunção booleana normal entre o caso 'reflexivo' e o 'recursivo', ordobra-o sobre uma lista, ou seja, verifica se há algum O elemento da lista é verdadeiro.)

Agora, uma relação está obviamente correta se for reflexiva. Estritamente falando, não, a potatonão possui um potato(e nem sequer um no sentido 'é' é usado aqui, como em 'Um policial é um policial'), mas essa é apenas a condição de término que cobre todos os relacionamentos após andar pela árvore (o que, diferentemente das árvores reais , significa "em direção às folhas").

Em todos os outros casos, tentamos extrair uma tupla knowledge(depois de termos filtereditado para ter certeza de que "vemos" apenas pares com o mesmo primeiro elemento que queremos verificar) e prosseguimos de onde ela aponta. A compreensão da lista lida com todas as tuplas possíveis para continuar e chama verifynovamente em cada caso. Um beco sem saída terá apenas uma lista vazia aqui e retornará em falsegeral, e assim não influenciará a instância da verifyqual foi chamada.

Leif Willerts
fonte
Você poderia adicionar uma breve explicação para pessoas fluentes não-haskell?
J Atkins
Alegremente! Eu apenas não faço isso para cada post até ser solicitado.
Leif Willerts
Ok, obrigada! (filler)
J Atkin
Uau, isso é uma explicação.
J Atkins
2
Acabei de ler a primeira metade Learn you a haskell for great good!e agora eu entendo isso! (Esta resposta é, na verdade, o que me levou a aprender mais sobre Haskell e FP, e é muuuito legal!)
J Atkin
4

JavaScript, 265 263 bytes

for(o={};i=prompt().split(/\W/);)a=i[0],b=i[1],d=i[2],b=="is"?((o[a]=o[a]||{p:[],k:{}}).p.push(d),o[d]=o[d]||{p:[],k:{}}):b=="has"?o[a].k[d]=1:alert(o[b]&&(a>"E"?b==d|(c=n=>~(p=o[n].p).indexOf(d)|p.some(c))(b):(c=n=>o[n].k.hasOwnProperty(i[4])|o[n].p.some(c))(b)))

Digite uma sequência em branco para sair.

Explicação

for(
  o={};                               // o = all objects
  i=prompt().split(/\W/);             // i = command as an array of words
)
  a=i[0],                             // a = first word
  b=i[1],                             // b = second word
  //c=i[2],                           // c = third word
  d=i[3],                             // b = fourth word
  //e=i[4],                           // e = fifth word

  // Case: <name> is a <name>.
  b=="is"?(
    (o[a]=o[a]||{p:[],k:{}})          // create the object if it does not exist
      .p.push(d),                     // add the parent to the object's list of parents
    o[d]=o[d]||{p:[],k:{}}            // create the parent if it does not exist
  ):

  // Case: <name> has a <name>.
  b=="has"?
    o[a].k[d]=1                       // set the specified property

  :
  alert(                              // display the responses to the questions
    o[b]                              // false if the queried object does not exist
    &&(

      // Case: Is <name> a <name>?
      a>"E"?                          // "Is" > "E" and "Does" < "E"
        b==d                          // check if it is itself
        |(c=n=>
          ~(p=o[n].p)                 // p = direct parents of current object
            .indexOf(d)               // check direct parents for the object
          |p.some(c)                  // check the grandparents
        )(b)

      // Case: Does <name> have a <name>?
      :
        (c=n=>
          o[n].k.hasOwnProperty(i[4]) // check if this object has the property
          |o[n].p.some(c)             // check it's parents for the property also
        )(b)
    )
  )
user81655
fonte
Você poderia usar string.split(" ");?
J Atkins
@JAtkin Eu costumava .match(/\w+/g)remover a pontuação das palavras.
user81655
Vi isso, mas não .split(" ")seria mais curto ou estou perdendo alguma coisa? (Eu não sei javascript)
J Atkin
@ JAtkin Se eu usasse .spliteu também teria que usar .slice(0,-1)(duas vezes) porque B is a A.faria Bherdar A.(com o .).
user81655
@JAtkin Na verdade, acabei de descobrir que a divisão aceita expressões regulares para que eu possa usar .split(/\W/). Obrigado por me fazer procurar isso!
user81655