Registros são estruturas de dados que agrupam informações relacionadas com rótulos. Eles são semelhantes a objetos em JavaScript ou Java, ou a structs em C ou Rust, mas com algumas diferenças importantes.
Os registros são criados com chaves e seus elementos são separados por vírgulas.
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
O tipo de um registro também é definido com uma sintaxe semelhante, exceto que os sinais de igual = são substituídos por dois-pontos :.
firefly : { name : String, creator : String, episodes : Int }
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
Repetir essas definições de tipo é trabalhoso, então usar um type alias para compartilhar a definição é uma prática comum.
type alias TvSeries =
{ name : String
, creator : String
, episodes : Int
}
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
O type alias também fornece automaticamente uma função construtora para o registro. Isso às vezes surpreende, já que funções em Elm devem começar com uma letra minúscula, mas construtores de tipo são uma exceção.
firefly : TvSeries
firefly = TvSeries "Firefly" "Joss Whedon" 14
A principal forma de acessar o valor de um campo de uma instância de registro é usar a notação de ponto, como firefly.creator.
O compilador Elm também oferece suporte a funções de acesso especiais que começam com um ponto ., como .creator, que funcionam em qualquer registro com um campo desse nome.
Essa é a segunda exceção à regra de que funções devem começar com uma letra minúscula.
Na verdade, funções definidas pelo usuário devem começar com uma letra minúscula, mas os dois exemplos que acabamos de ver são gerados pelo compilador, não pelo programador.
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
firefly.name
--> "Firefly"
.creator firefly
--> "Joss Whedon"
Registros também podem ser desestruturados em associações usando a correspondência de padrões de registro. A desestruturação funciona com qualquer registro que tenha campos com os nomes relevantes.
episodesCount : TvSeries -> Int
episodesCount { episodes } =
episodes
complicatedCopy : TvSeries -> TvSeries
complicatedCopy show =
let
{ name, creator } = show
in
{ name = name
, creator = creator
, episodes = episodesCount show
}
Registros são imutáveis, como tudo em Elm, então, depois que um registro é definido, os valores de seus campos nunca podem mudar.
Há uma sintaxe especial para criar uma cópia de um registro existente, mas alterando um ou mais campos.
Essa sintaxe usa o símbolo de barra vertical | para distinguir o registro original e os campos que serão alterados, como { oldRecord | field1 = newValue }.
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
updatedFirefly : TvSeries
updatedFirefly =
{ firefly | creator = "J Whedon", episodes = 15 }
Elm usa [igualdade estrutural][equality], o que significa que duas instâncias do mesmo registro com valores idênticos são iguais.
firefly1 = TvSeries "Firefly" "Joss Whedon" 14
firefly2 = TvSeries "Firefly" "Joss Whedon" 14
firefly1 == firefly2
--> True
Elm também oferece suporte à tipagem estrutural, o que significa que, se uma função exige um registro com campos x e y, ela funcionará com qualquer registro que tenha esses campos, como pontos 2D, pontos 3D, naves espaciais, etc.
Esses tipos de registro têm uma barra vertical | em sua definição, como { a | x : Float, y : Float }, e são chamados de "registros extensíveis" na terminologia de Elm.
Cuidado com a diferença entre uma barra vertical | em uma definição de tipo, que é uma definição de registro extensível ({ a | x : Int }), e uma barra vertical | em uma instância de registro real, que significa que estamos atualizando alguns campos desse registro.
point2d = { x = 1, y = 2 }
point3d = { x = 3, y = 4, z = 7 }
.x point2d --> 1
.x point3d --> 3
length : { a | x : Float, y : Float } -> Float
length vector =
sqrt (vector.x * vector.x + vector.y * vector.y)
length point2d --> 2.236068
length point3d --> 5
translateX : { a | x : Float } -> { a | x : Float }
translateX vector =
{ vector | x = vector.x + 1 }
translateX point2d --> { x = 2, y = 2 }
translateX point3d --> { x = 4, y = 4, z = 7 }
Neste exercício, você é um grande fã de esportes e acabou de descobrir uma paixão pelo basquete da NBA. Como você é novo no basquete da NBA, está mergulhando fundo na história da NBA, acompanhando times, treinadores, suas estatísticas de vitórias e derrotas, e comparando uns com os outros.
Como você ainda não tem um time favorito, também vai desenvolver um algoritmo para descobrir se deve torcer por um determinado time. Você tem sete tarefas para ajudar a desenvolver seu algoritmo proprietário de torcer por um time.
Defina o alias de tipo Coach para um record com os dois campos a seguir, nesta ordem:
name: o nome do treinador, do tipo String.formerPlayer: indica se o treinador já foi jogador, do tipo Bool.Defina o alias de tipo Stats para um record com os dois campos a seguir, nesta ordem:
wins: o número de vitórias, do tipo Int.losses: o número de derrotas, do tipo Int.Defina o alias de tipo Team para um record com os três campos a seguir, nesta ordem:
name: o nome do time, do tipo String.coach: o treinador do time, do tipo Coach.stats: as estatísticas do time, do tipo Stats.Você quer integrar com um código de terceiros que tem o conceito de time, mas que chama o seu código com os parâmetros em uma ordem diferente. Adicione uma função createTeam com a seguinte assinatura String -> Stats -> Coach -> Team.
Como os donos de times da NBA são impacientes, você percebeu que maus resultados do time muitas vezes levavam à substituição do treinador.
Implemente a função replaceCoach, que recebe o novo treinador e o time como parâmetros e retorna o time, mas com o novo treinador:
let
coach = { name = "Larry Bird", formerPlayer = True }
stats = { wins = 58, losses = 24 }
team = { name = "Indiana Pacers", coach = coach, stats = stats }
newCoach = { name = "Isiah Thomas", formerPlayer = True }
in
replaceCoach newCoach team
--> { name = "Indiana Pacers"
-- , coach = { name = "Isiah Thomas", formerPlayer = True }
-- , stats = { wins = 58, losses = 24 } }
No futuro, você planeja criar um algoritmo complicado para decidir se deve torcer por um time ou não, mas, nesta primeira tentativa, você decidiu que deve torcer por qualquer time que tenha mais vitórias do que derrotas.
Você quer que essa função seja reutilizável em outros contextos, então use um record extensível na assinatura de tipo para aceitar qualquer record que tenha os campos relevantes.
Para manter a função o mais concisa possível, você quer usar pattern matching para obter o stats do parâmetro da função.
Implemente a função rootForTeam, que recebe um time e retorna True se você deve torcer por esse time; caso contrário, retorne False.
let
spursCoach = { name = "Gregg Popovich", formerPlayer = False }
spursStats = { wins = 56, losses = 26 }
spursTeam = { name = "San Antonio Spurs", coach = spursCoach, stats = spursStats }
in
rootForTeam spursTeam
--> True
Crie sua conta no Exercism para aprender e dominar Elm com 28 conceitos110 exercícios e mentoria humana de verdade, tudo de graça.