Los ejercicios de concepto son ejercicios diseñados para enseñar conceptos (de programación) específicos. Los conceptos que enseñan los ejercicios de concepto forman un temario. Para obtener más información sobre cómo diseñar un temario, consulta la documentación sobre el temario.
Puedes generar rápidamente el esqueleto de un nuevo ejercicio de concepto ejecutando los siguientes comandos desde el directorio raíz del track:
bin/fetch-configlet
bin/configlet create --concept-exercise <slug>
Para obtener más información, consulta la documentación de configlet create
Los metadatos de un ejercicio de concepto se definen en la clave exercises.concept del archivo config.json. Los metadatos definen el UUID, el slug y otros datos del ejercicio.
{
"exercises": {
"concept": [
{
"uuid": "93fbc7cf-3a7e-4450-ad22-e30129c36bb9",
"slug": "cars-assemble",
"name": "Cars, Assemble!",
"concepts": ["if-statements", "numbers"],
"prerequisites": ["basics"]
}
]
}
}
Cada ejercicio de concepto tiene su propio directorio dentro del directorio exercises/concept del track. El nombre del directorio del ejercicio de concepto debe coincidir con la propiedad slug del ejercicio de concepto, tal como se define en el archivo config.json.
Un ejercicio de concepto tiene cuatro tipos de archivos:
Estos archivos se muestran al estudiante para ayudarle a entender el ejercicio.
.docs/introduction.md: presenta al estudiante el concepto o los conceptos que enseña el ejercicio (obligatorio).docs/instructions.md: proporciona las instrucciones del ejercicio (obligatorio).docs/hints.md: proporciona pistas al estudiante para ayudarle a desbloquearse en un ejercicio (obligatorio)Estos archivos no se muestran al estudiante, sino que se usan para definir los metadatos del ejercicio.
.meta/config.json: contiene metainformación sobre el ejercicio (obligatorio).meta/design.md: describe el diseño del ejercicio (obligatorio)Estos archivos describen enfoques para el ejercicio.
.approaches/introduction.md: introducción a los enfoques más comunes para el ejercicio (opcional).approaches/config.json: metadatos de los enfoques (opcional).approaches/<approach-slug>/content.md: descripción del enfoque (opcional).approaches/<approach-slug>/snippet.txt: fragmento que muestra el enfoque (opcional)Estos archivos describen artículos para el ejercicio.
.articles/config.json: metadatos de los artículos (opcional).articles/<article-slug>/content.md: descripción del artículo (opcional).articles/<article-slug>/snippet.md: fragmento que muestra el artículo (opcional)Los archivos específicos del lenguaje, como los archivos de implementación y de test. Los nombres de estos archivos son específicos de cada track.
exercises
└── concept
└── cars-assemble
├── .approaches
| ├── for-loop
| | ├── content.md
| | └── snippet.txt
| ├── config.json
| └── introduction.md
├── .articles
| ├── performance
| | ├── content.md
| | └── snippet.md
| └── config.json
├── .docs
| ├── introduction.md
| ├── instructions.md
| └── hints.md
├── .meta
| ├── config.json
| ├── design.md
| └── Exemplar.cs (implementación ejemplar)
├── CarsAssemble.cs (implementación stub)
└── CarsAssemblyTests.cs (tests)
Preferimos un enfoque de «fusión optimista» para los ejercicios nuevos, en el que los tracks pueden desarrollar ejercicios en estado de «trabajo en curso». El estado mínimo válido, que pasa configlet y te permite fusionar, es:
config.json del track, con el status establecido en wip..meta/config.json válido.docs/introduction.md.docs/instructions.md.docs/hints.md.docs/introduction.md
Objetivo: Presentar al estudiante el concepto o los conceptos que enseña el ejercicio.
Presencia: Obligatorio
about.md del concepto.Por ejemplo, la introducción a un ejercicio de «strings» podría describir un string como una «secuencia de caracteres Unicode» o una «serie de bytes», explicar a los usuarios cómo crear un string y explicar que un string tiene métodos que se pueden usar para manipularlo. A menos que el estudiante necesite entender detalles más matizados para resolver el ejercicio, este tipo de explicación breve (junto con un ejemplo de su sintaxis) debería ser información suficiente para que el estudiante resuelva el ejercicio.
# Introduction
There are two primary ways to assign objects to names in Ruby - using variables or constants. Variables are always written in snake case. A variable can reference different objects over its lifetime. For example, `my_first_variable` can be defined and redefined many times using the `=` operator:
```ruby
my_first_variable = 1
my_first_variable = "Some string"
my_first_variable = SomeComplexObject.new
```
.docs/introduction.md.tpl
Objetivo: Plantilla a partir de la cual generar un archivo introduction.md.
Presencia: Opcional
El documento introduction.md presenta al estudiante el concepto o los conceptos del ejercicio. Cada concepto también tiene su propio documento introduction.md, que no se muestra fuera del contexto de un ejercicio.
Si la introducción del concepto debe incluirse literalmente en la introducción del ejercicio, se puede usar un archivo introduction.md.tpl. Este archivo permite hacer referencia a las introducciones de los conceptos mediante marcadores de posición: %{concept:<concept-slug>}.
configlet puede generar un archivo introduction.md a partir de un archivo de plantilla. El archivo generado tendrá los marcadores de posición de conceptos reemplazados por el contenido de la introduction del concepto.
El sitio web de Exercism solo conoce el documento introduction.md. Es responsabilidad del track generar el introduction.md cuando se usa un archivo de plantilla.
Los tracks pueden decidir para cada ejercicio si usan una plantilla o no. En algunos casos, usar la introducción del concepto literalmente puede no ser lo óptimo. Opta siempre por lo que proporcione la mejor experiencia de aprendizaje al estudiante.
# Introduction
%{concept:variables}
.docs/instructions.md
Objetivo: Proporcionar las instrucciones del ejercicio.
Presencia: Obligatorio
Este archivo se divide en dos partes.
Cada tarea debe cumplir el siguiente estándar:
## 1. Do X, ## 2. Do Y).## 1. Check if an appointment has already passed).Implement method X(...) that takes an A and returns a Z),Damos mucha importancia a que el contenido de Exercism sea seguro para todo el mundo, por lo que a menudo pecamos de cautelosos al decidir si una historia es apropiada o no. Aunque tenemos cuidado con lo que fusionamos, entendemos que es difícil ser consciente de lo que puede resultar problemático, así que siempre asumiremos que actúas de buena fe y haremos todo lo posible por detectar cualquier problema en la revisión de forma no confrontativa. Si quieres revisar una historia con nosotros, menciona a @exercism/leadership y la veremos juntos. Aquí tienes algunos puntos orientativos:
# Instructions
In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book.
## 1. Calculate the remaining oven time in minutes
Define the `Lasagna#remaining_minutes_in_oven` method that takes the actual minutes the lasagna has been in the oven as a parameter and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task.
```ruby
lasagna = Lasagna.new
lasagna.remaining_minutes_in_oven(30)
# => 10
```
.docs/hints.md
Objetivo: Proporcionar pistas al estudiante para ayudarle a desbloquearse en un ejercicio.
Presencia: Obligatorio
## General.instructions.md (p. ej., ## 2. Do Y).## 2. Check if a book can be borrowed).Implement the 'canBorrowBook' function to check if a book can be borrowed. The function takes a book as its parameter and returns `true` if the book has not already been borrowed; otherwise, return `false`).Ver las pistas no será un camino «recomendado» y (suavemente) desaconsejaremos usarlas a menos que el estudiante no pueda avanzar sin ellas. Por ello, conviene tener en cuenta que el estudiante que las lea estará algo confundido o abrumado, y quizá frustrado.
# Hints
## General
- You need to define a [constant][constant] which should contain the [integer][integers] value specified in the recipe.
## 1. Calculate the remaining oven time in minutes
- You need to define a [method][methods] with a single parameter for the actual time so far.
[constants]: https://www.rubyguides.com/2017/07/ruby-constants/
[integers]: https://ruby-doc.org/core-2.7.0/Integer.html
[methods]: https://launchschool.com/books/ruby/read/methods
.meta/design.md
Objetivo: Describir el diseño del ejercicio.
Presencia: Obligatorio
Este archivo contiene información sobre el diseño del ejercicio, lo que incluye cosas como su objetivo, sus metas didácticas, qué no enseñar y más. Esta información se puede extraer del issue de GitHub correspondiente al ejercicio.
Existe para informar a futuros mantenedores o colaboradores sobre el alcance y las limitaciones de un ejercicio, y para evitar la tendencia natural a volver los ejercicios más complejos con el tiempo.
# Design
## Goal
The goal of this exercise is to teach the student the basics of programming in Ruby.
## Learning objectives
- Know what a variable is.
- Know how to define a variable.
- Know how to update a variable.
## Out of scope
- Memory and performance characteristics.
- Method overloads.
## Concepts
The Concepts this exercise unlocks are:
- `basics`: know what a variable is; know how to define a variable; know how to update a variable.
## Prerequisites
There are no prerequisites.
.meta/config.json
Objetivo: Contener metainformación sobre el ejercicio.
Presencia: Obligatorio
Este archivo contiene metainformación sobre el ejercicio:
authors: El nombre o nombres de usuario de GitHub del autor o autores del ejercicio (obligatorio)
contributors: El nombre o nombres de usuario de GitHub del colaborador o colaboradores del ejercicio (opcional)
forked_from: De qué ejercicio o ejercicios se bifurcó (obligatorio si el ejercicio es una bifurcación)files: Las ubicaciones de los archivos usados en este ejercicio, relativas al directorio del ejercicio (obligatorio)
solution: el o los archivos de implementación stub (obligatorio)test: el o los archivos de test (obligatorio)exemplar: el o los archivos de implementación ejemplar (obligatorio)editor: otros archivos que se muestran como de solo lectura en el editor (opcional)invalidator: archivos que, cuando se modifican, hacen que una solución quede obsoleta (opcional)language_versions: Los requisitos de versión del lenguaje (opcional)blurb: Una descripción breve de este ejercicio. Su longitud debe ser <= 350. Markdown no es compatible (obligatorio)source: La fuente en la que se basa este ejercicio (opcional)source_url: La URL de la fuente en la que se basa este ejercicio (opcional)representer: Metainformación relacionada con cómo el representer procesa este archivo (opcional)
version: Un entero con la versión del representer que se usará para el ejercicio (obligatorio si la clave padre está presente)icon: El slug del icono (consulta la lista completa de iconos). Si no se especifica, se usará el slug del ejercicio (opcional)custom: Cualquier dato no estándar específico del ejercicio. Se puede usar para personalizar el comportamiento de las herramientas del track por ejercicio (opcional)Si alguien es autor y colaborador a la vez, inclúyelo solo como autor.
{
"authors": ["FSharpForever"],
"files": {
"solution": ["Lasagna.fs"],
"test": ["LasagnaTests.fs"],
"exemplar": [".meta/Exemplar.fs"]
},
"blurb": "Learn the basics of F# by cooking Lucian's Luscious Lasagna"
}
Supongamos que el usuario FSharpForever ha escrito un ejercicio llamado log-levels para el track de F#. PythonProfessor adapta el ejercicio para el track de Python. Más tarde, el usuario GladToHelp mejora el ejercicio.
{
"authors": ["PythonProfessor"],
"contributors": ["GladToHelp"],
"files": {
"solution": ["log_levels.py"],
"test": ["log_levels_test.py"],
"exemplar": [".meta/exemplar.py"],
"editor": ["test_helper.py"]
},
"forked_from": ["fsharp/log-levels"],
"language_versions": ">=3.7",
"blurb": "Learn how to work with strings by processing log lines.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Log_file",
"representer": {
"version": 2
},
"icon": "logs",
"custom": {
"parallel": true
}
}
Ten en cuenta que:
forked_from sea correcto.language_versions es un string de formato libre que los tracks pueden usar e interpretar como quieran..approaches/introduction.md
Objetivo: Introducción a los enfoques más comunes para el ejercicio
Presencia: Opcional
Este archivo describe los enfoques más comunes para el ejercicio. Consulta la documentación para obtener más información sobre qué debe incluir este archivo.
# Introduction
The key to this exercise is to deal with C# strings being immutable, which means that a `string`'s value cannot be changed.
Therefore, to reverse a string you'll need to create a _new_ `string`.
## Using LINQ
```csharp
public static string Reverse(string input)
{
return new string(input.Reverse().ToArray());
}
```
For more information, check the [LINQ approach][approach-linq].
## Which approach to use?
If readability is your primary concern (and it usually should be), the LINQ-based approach is hard to beat.
.approaches/config.json
Objetivo: Metadatos de los enfoques
Presencia: Opcional (obligatorio cuando existe una introducción de enfoque o un enfoque)
Este archivo contiene metainformación sobre los enfoques del ejercicio:
introduction: El nombre o nombres de usuario de GitHub del autor o autores de la introducción de los enfoques del ejercicio (opcional)
authors: El nombre o nombres de usuario de GitHub del autor o autores de la introducción de los enfoques del ejercicio (obligatorio)
contributors: El nombre o nombres de usuario de GitHub del colaborador o colaboradores de la introducción de los enfoques del ejercicio (opcional)
approaches: Un array que lista los enfoques detallados (opcional)
uuid: un UUID V4 que identifica de forma única el enfoque. El UUID debe ser único tanto dentro del track como en todos los tracks, y nunca debe cambiarslug: el slug del enfoque, que es un string en minúsculas con formato kebab-case. El slug debe ser único entre todos los slugs de enfoques del track. Su longitud debe ser <= 255.title: el título del enfoque. Su longitud debe ser <= 255.blurb: Una descripción breve de este enfoque. Su longitud debe ser <= 350. Markdown no es compatible (obligatorio)authors: El nombre o nombres de usuario de GitHub del autor o autores del enfoque del ejercicio (obligatorio)
contributors: El nombre o nombres de usuario de GitHub del colaborador o colaboradores del enfoque del ejercicio (opcional)
tags: Especifica las condiciones para que un envío se vincule a un enfoque. (opcional)
all: Un array de tags que deben estar todos presentes en un envío (opcional, a menos que any no tenga elementos)any: Un array de tags de los que al menos uno debe estar presente en un envío (opcional, a menos que all no tenga elementos)not: ninguno de los tags debe estar presente en un envío (opcional){
"introduction": {
"authors": ["erikschierboom"]
},
"approaches": [
{
"uuid": "448fb2b4-18ab-4e55-aa54-ad4ed6d5f7f6",
"slug": "span",
"title": "Use Span<T>",
"blurb": "Use Span<T> to efficiently reverse a string.",
"authors": ["erikschierboom"]
}
]
}
.approaches/<approach-slug>/content.md
Objetivo: Descripción detallada del enfoque
Presencia: Opcional (obligatorio para los enfoques)
Este archivo contiene una descripción detallada del enfoque. Consulta la documentación para obtener más información sobre qué debe incluir este archivo.
# Span
```csharp
Span<char> chars = stackalloc char[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
```
This `Span<T>` approach uses a `for` loop.
.approaches/<approach-slug>/snippet.txt
Objetivo: Fragmento que muestra el enfoque
Presencia: Opcional (obligatorio para los enfoques)
Este archivo contiene un pequeño fragmento que muestra el enfoque. El fragmento se muestra en la página Dig Deeper de un ejercicio.
Su número de líneas debe ser <= 8.
Consulta la documentación para obtener más información sobre qué debe incluir este archivo.
Span<char> chars = stackalloc char[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
.article/config.json
Objetivo: Metadatos de los artículos
Presencia: Opcional (obligatorio cuando existe un artículo)
Este archivo contiene metainformación sobre los artículos del ejercicio:
articles: Un array que lista los artículos detallados (opcional)
uuid: un UUID V4 que identifica de forma única el artículo. El UUID debe ser único tanto dentro del track como en todos los tracks, y nunca debe cambiarslug: el slug del artículo, que es un string en minúsculas con formato kebab-case. El slug debe ser único entre todos los slugs de artículos del track. Su longitud debe ser <= 255.title: el título del artículo. Su longitud debe ser <= 255.blurb: Una descripción breve de este artículo. Su longitud debe ser <= 350. Markdown no es compatible (obligatorio)authors: El nombre o nombres de usuario de GitHub del autor o autores del artículo del ejercicio (obligatorio)
contributors: El nombre o nombres de usuario de GitHub del colaborador o colaboradores del artículo del ejercicio (opcional)
{
"articles": [
{
"uuid": "6db71962-62d5-448b-a980-c20ae41013ed",
"slug": "performance",
"title": "Optimizing performance",
"blurb": "Explore how to most efficiently reverse a string and what the trade-offs are.",
"authors": ["erikschierboom"]
}
]
}
.articles/<article-slug>/content.md
Objetivo: Descripción detallada del enfoque
Presencia: Opcional (obligatorio para los enfoques)
Este archivo contiene una descripción detallada del enfoque. Consulta la documentación para obtener más información sobre qué debe incluir este archivo.
# Performance
In this document, we'll find out which approach is the most performant one.
## Benchmark results
| Method | Mean | Error | StdDev | Median | Allocated |
| -----: | --------: | --------: | --------: | --------: | --------: |
| Linq | 29.133 ns | 0.5865 ns | 0.5486 ns | 28.984 ns | 80 B |
| Array | 4.806 ns | 0.4999 ns | 1.4739 ns | 3.967 ns | - |
.articles/<article-slug>/snippet.txt
Objetivo: Fragmento que muestra el enfoque
Presencia: Opcional (obligatorio para los artículos)
Este archivo contiene un pequeño fragmento que muestra el artículo. El fragmento se muestra en la página Dig Deeper de un ejercicio.
Su número de líneas debe ser <= 8.
Consulta la documentación para obtener más información sobre qué debe incluir este archivo.
| Method | Mean | Allocated |
| -----: | --------: | --------: |
| Linq | 29.133 ns | 80 B |
| Array | 4.806 ns | - |
Objetivo: Proporcionar un punto de partida para los estudiantes.
Presencia: Obligatorio
"files.solution" del archivo .meta/config.json.class Lasagna
def remaining_minutes_in_oven(actual_minutes_in_oven)
raise NotImplementedError, 'Please implement the Lasagna#remaining_minutes_in_oven method'
end
def preparation_time_in_minutes(layers)
raise NotImplementedError, 'Please implement the Lasagna#preparation_time_in_minutes method'
end
end
Objetivo: Verificar que una solución es correcta.
Presencia: Obligatorio
instructions.md."files.test" del archivo .meta/config.json.require 'minitest/autorun'
require_relative 'lasagna'
class LasagnaTest < Minitest::Test
def test_remaining_minutes_in_oven
assert_equal 15, Lasagna.new.remaining_minutes_in_oven(25)
end
def test_preparation_time_in_minutes_with_one_layer
assert_equal 2, Lasagna.new.preparation_time_in_minutes(1)
end
def test_preparation_time_in_minutes_with_multiple_layers
assert_equal 8, Lasagna.new.preparation_time_in_minutes(4)
end
end
Objetivo: Proporcionar la implementación objetivo a la que el estudiante debe aspirar.
Presencia: Obligatorio
"files.exemplar" del archivo .meta/config.json.class Lasagna
EXPECTED_MINUTES_IN_OVEN = 40
PREPARATION_MINUTES_PER_LAYER = 2
def remaining_minutes_in_oven(actual_minutes_in_oven)
EXPECTED_MINUTES_IN_OVEN - actual_minutes_in_oven
end
def preparation_time_in_minutes(layers)
layers * PREPARATION_MINUTES_PER_LAYER
end
end
Objetivo: Garantizar que los tests se puedan ejecutar.
Presencia: Obligatorio si los archivos predeterminados no bastan para ejecutar los tests
Algunos lenguajes requieren archivos adicionales para que los tests se ejecuten. Ejemplos de ello son los archivos de proyecto de C# y los archivos package.json de Node, sin los cuales no será posible ejecutar los tests.
Algunos archivos no son específicos de ejercicios individuales, sino que se aplican a todos los ejercicios. Consulta la documentación para obtener más información.
Los ejercicios de concepto deben llevar el nombre de su historia o tema, no de su concepto o conceptos.
Buenos ejemplos de nombres:
Tim from MarketingLucian's Luscious LasagnaCalculator ConundrumNombres no permitidos:
Booleans: usa un nombre de concepto, no un nombre de historiaExercise #1: un ejercicio no es una historia ni un temaCuando bifurques un ejercicio sin cambios importantes, usa el nombre original siempre que sea posible.
Cada ejercicio también tiene un slug, que es una versión normalizada del nombre del ejercicio según las siguientes reglas:
[a-z0-9-]+)two-fer antes que 2-fer)Buenos ejemplos de slugs:
tim-from-marketinglucians-luscious-lasagnacalculator-conundrumSlugs no permitidos:
TIM-FROM-MARKETING: no usa minúsculas (es decir, tim-from-marketing)TimFromMarketing: no usa kebab-case (es decir, tim-from-marketing)floating-point-numbers: usa un nombre de concepto, no un nombre de historiaHay una diferencia en cómo se presenta la documentación de un ejercicio al estudiante cuando usa el editor del navegador frente a cuando usa la CLI. Consulta este documento para obtener más información.
Cada ejercicio tiene un icono asociado.
Por defecto, el icono mostrado es el cuyo nombre coincide con el slug del ejercicio.
Se puede sobrescribir especificando la propiedad icon en el archivo .meta/config.json del ejercicio.
Si estás bifurcando un ejercicio existente, probablemente ya haya un icono para ese ejercicio. Si no es así, abre un issue en el repositorio website-icons.