Os registos são estruturas de dados que agrupam informação relacionada com etiquetas. São semelhantes a objetos em JavaScript ou Java, ou a structs em C ou Rust, mas com algumas diferenças importantes.
Os registos são criados com chavetas e os seus elementos são separados por vírgulas.
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
O tipo de um registo também se define 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 estas definições de tipo é maçador, por isso usar um type alias para partilhar 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 registo. Isto por vezes surpreende, porque as funções em Elm têm de começar por um caráter minúsculo, mas os construtores de tipo são uma exceção.
firefly : TvSeries
firefly = TvSeries "Firefly" "Joss Whedon" 14
A forma principal de aceder ao valor de um campo de uma instância de um registo é usar a notação de ponto, como firefly.creator.
O compilador de Elm também suporta funções de acesso especiais que começam por um ponto ., como .creator, que funcionam em qualquer registo que tenha um campo com esse nome.
É a segunda exceção à regra de que as funções têm de começar por um caráter minúsculo.
Na verdade, as funções definidas pelo programador têm de começar por um caráter minúsculo, mas os dois exemplos que acabámos 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"
Os registos também podem ser desestruturados em ligações, usando a correspondência de padrões de registos. A desestruturação funciona com qualquer registo 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
}
Os registos são imutáveis, tal como tudo em Elm, por isso, depois de um registo estar definido, os valores dos seus campos nunca podem mudar.
Existe uma sintaxe especial para criar uma cópia de um registo existente, alterando um ou mais campos.
Esta sintaxe usa o símbolo de barra vertical | para distinguir o registo original dos campos que vão mudar, como em { 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 registo 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 suporta tipagem estrutural, ou seja, se uma função exige um registo com um campo x e um campo y, vai funcionar com qualquer registo que tenha esses campos, como pontos 2D, pontos 3D, naves espaciais, etc.
Esses tipos de registo têm uma barra vertical | na sua definição, como { a | x : Float, y : Float }, e são chamados "registos extensíveis" na terminologia de Elm.
Tem atenção à diferença entre uma barra vertical | numa definição de tipo, que é uma definição de registo extensível ({ a | x : Int }), e uma barra vertical | numa instância de registo real, que significa que estamos a atualizar alguns campos desse registo.
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 és um grande fã de desporto e acabaste de descobrir uma paixão pelo basquetebol da NBA. Como és novo no basquetebol da NBA, estás a estudar a história da NBA a fundo. Acompanhas equipas e treinadores, manténs as estatísticas de vitórias e derrotas e comparas tudo entre si.
Como ainda não tens uma equipa favorita, também vais desenvolver um algoritmo para descobrir se deves torcer por uma equipa em particular. Tens sete tarefas para te ajudar a desenvolver o teu algoritmo proprietário root-for-a-team.
Define o alias de tipo Coach para um registo com os dois campos seguintes, pela ordem indicada:
name: o nome do treinador, do tipo String.formerPlayer: indica se o treinador foi jogador, do tipo Bool.Define o alias de tipo Stats para um registo com os dois campos seguintes, pela ordem indicada:
wins: o número de vitórias, do tipo Int.losses: o número de derrotas, do tipo Int.Define o alias de tipo Team para um registo com os três campos seguintes, pela ordem indicada:
name: o nome da equipa, do tipo String.coach: o treinador da equipa, do tipo Coach.stats: as estatísticas da equipa, do tipo Stats.Queres interagir com código de terceiros que tem o conceito de equipa, mas que chama o teu código com os parâmetros numa ordem diferente. Adiciona uma função createTeam com a seguinte assinatura String -> Stats -> Coach -> Team.
Como os donos da NBA são impacientes, descobriste que maus resultados da equipa levavam muitas vezes à substituição do treinador.
Implementa a função replaceCoach que recebe o novo treinador e a equipa como parâmetros, e devolve a equipa 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, planeias elaborar um algoritmo complexo sobre se deves torcer por uma equipa ou não, mas, para a primeira tentativa, decidiste que deves torcer por qualquer equipa que tenha mais vitórias do que derrotas.
Queres que esta função seja reutilizável noutros contextos, por isso usa um registo extensível na assinatura de tipo para aceitar qualquer registo que tenha os campos relevantes.
Para manteres a função o mais concisa possível, queres usar correspondência de padrões para obter o campo stats do parâmetro da função.
Implementa a função rootForTeam que recebe uma equipa e devolve True se deves torcer por essa equipa; caso contrário, devolve 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
Inscreve-te no Exercism para aprenderes e dominares Elm com 28 conceitos110 exercícios, e mentoria humana real, tudo grátis.