Los ejercicios de conceptos son ejercicios diseñados para enseñar conceptos específicos (de programación). Los conceptos que enseñan los ejercicios de conceptos forman un temario. Para más información sobre cómo diseñar un temario, consulta la documentación sobre el temario.
Puedes crear rápidamente el esqueleto de un nuevo ejercicio de conceptos ejecutando los siguientes comandos desde el directorio raíz del track:
bin/fetch-configlet
bin/configlet create --concept-exercise <slug>
Para más información, consulta la documentación de configlet create
Los metadatos de un ejercicio de conceptos se definen en la clave exercises.concept del archivo config.json. Los metadatos definen el UUID, el slug y más cosas 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 conceptos tiene su propio directorio dentro del directorio exercises/concept del track. El nombre del directorio del ejercicio de conceptos debe coincidir con la propiedad slug del ejercicio de conceptos, tal como se define en el archivo config.json.
Un ejercicio de conceptos tiene cuatro tipos de archivos:
Estos archivos se le presentan 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 desatascarse en un ejercicio (obligatorio)Estos archivos no se le presentan 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 pruebas. 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 (exemplar implementation)
├── CarsAssemble.cs (stub implementation)
└── CarsAssemblyTests.cs (tests)
Preferimos un enfoque de «fusión optimista» para los ejercicios nuevos, en el que los tracks pueden desarrollar ejercicios en un estado de «trabajo en curso». El estado mínimo válido, que pasará configlet y te permitirá 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
Propósito: Presentar al estudiante el concepto o los conceptos que enseña el ejercicio.
Presencia: Obligatorio
about.md del concepto.A modo de ejemplo, la introducción de un ejercicio de «strings» podría describir un string como solo una «secuencia de caracteres Unicode» o una «serie de bytes», decirle a quien lo lee cómo crear un string y explicar que un string tiene métodos que pueden usarse 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
Propósito: 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, puede usarse un archivo introduction.md.tpl. Este archivo permite referirse 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 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 podría no ser lo óptimo. Elige siempre lo que ofrezca la mejor experiencia de aprendizaje al estudiante.
# Introduction
%{concept:variables}
.docs/instructions.md
Propósito: 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),Valoramos mucho hacer que el contenido de Exercism sea seguro para todos y, por eso, a menudo pecamos de precavidos al decidir si una historia es apropiada o no. Aunque somos cuidadosos con lo que fusionamos, entendemos que es difícil ser consciente de lo que puede percibirse como problemático, así que siempre asumiremos que actúas de buena fe y haremos 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 revisaremos 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
Propósito: Proporcionar pistas al estudiante para ayudarle a desatascarse en un ejercicio.
Presencia: Obligatorio
## General.instructions.md (por ejemplo, ## 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 desaconsejaremos (sutilmente) su uso a menos que el estudiante no pueda avanzar sin ellas. Por eso conviene tener en cuenta que el estudiante que las lea estará un poco 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
Propósito: Describir el diseño del ejercicio.
Presencia: Obligatorio
Este archivo contiene información sobre el diseño del ejercicio, que incluye cosas como su objetivo, sus metas de enseñanza, qué no enseñar y más. Esta información puede extraerse 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 así evitar la tendencia natural a que los ejercicios se vuelvan cada vez 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
Propósito: Contiene metainformación sobre el ejercicio.
Presencia: Obligatorio
Este archivo contiene metainformación sobre el ejercicio:
authors: El nombre o los nombres de usuario de GitHub del autor o los autores del ejercicio (obligatorio)
contributors: El nombre o los nombres de usuario de GitHub del colaborador o los 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 prueba (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, al cambiar, hacen que una solución quede obsoleta (opcional)language_versions: Requisitos de versión del lenguaje (opcional)blurb: Una descripción breve de este ejercicio. Su longitud debe ser <= 350. No se admite Markdown (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 para 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 específico del ejercicio que no sea estándar. Puede usarse para personalizar el comportamiento de las herramientas del track por ejercicio (opcional)Si alguien es tanto autor como colaborador, lístalo 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 adelante, 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 una cadena de formato libre que los tracks pueden usar e interpretar como quieran..approaches/introduction.md
Propósito: 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 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
Propósito: Metadatos de los enfoques
Presencia: Opcional (obligatorio cuando existe una introducción a los enfoques o un enfoque)
Este archivo contiene metainformación sobre los enfoques del ejercicio:
introduction: El nombre o los nombres de usuario de GitHub del autor o los autores de la introducción a los enfoques del ejercicio (opcional)
authors: El nombre o los nombres de usuario de GitHub del autor o los autores de la introducción a los enfoques del ejercicio (obligatorio)
contributors: El nombre o los nombres de usuario de GitHub del colaborador o los colaboradores de la introducción a 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 una cadena en minúsculas y kebab-case. El slug debe ser único entre todos los slugs de enfoque 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. No se admite Markdown (obligatorio)authors: El nombre o los nombres de usuario de GitHub del autor o los autores del enfoque del ejercicio (obligatorio)
contributors: El nombre o los nombres de usuario de GitHub del colaborador o los colaboradores del enfoque del ejercicio (opcional)
tags: Especifica las condiciones para cuándo una solución enviada se vincula a un enfoque. (opcional)
all: Un array de etiquetas que deben estar todas presentes en una solución enviada (opcional, a menos que any no tenga elementos)any: Un array de etiquetas de las cuales al menos una debe estar presente en una solución enviada (opcional, a menos que all no tenga elementos)not: ninguna de las etiquetas debe estar presente en una solución enviada (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
Propósito: 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 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
Propósito: 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 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
Propósito: 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 una cadena en minúsculas y kebab-case. El slug debe ser único entre todos los slugs de artículo 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. No se admite Markdown (obligatorio)authors: El nombre o los nombres de usuario de GitHub del autor o los autores del artículo del ejercicio (obligatorio)
contributors: El nombre o los nombres de usuario de GitHub del colaborador o los 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
Propósito: 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 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
Propósito: 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 más información sobre qué debe incluir este archivo.
| Method | Mean | Allocated |
| -----: | --------: | --------: |
| Linq | 29.133 ns | 80 B |
| Array | 4.806 ns | - |
Propósito: 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
Propósito: Verificar la corrección de una solución.
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
Propósito: 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
Propósito: Garantizar que las pruebas puedan ejecutarse.
Presencia: Obligatorio si los archivos predeterminados no bastan para ejecutar las pruebas
Algunos lenguajes requieren archivos adicionales para que las pruebas se ejecuten. Ejemplos de estos son los archivos de proyecto de C# y los archivos package.json de Node, sin los cuales no será posible ejecutar las pruebas.
Algunos archivos no son específicos de ejercicios individuales, sino que se aplican a todos los ejercicios. Consulta la documentación para más información.
Los ejercicios de conceptos deben nombrarse según su historia/tema, no según 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/temaAl bifurcar un ejercicio sin cambios importantes, usa el nombre original cuando 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 en lugar de 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 del ejercicio al estudiante cuando se usa el editor en el navegador frente a cuando se usa la CLI. Consulta este documento para más información.
Cada ejercicio tiene un icono que lo acompaña.
Por defecto, el icono mostrado es el cuyo nombre coincide con el slug del ejercicio.
Se puede sobrescribir esto especificando la propiedad icon en el archivo .meta/config.json del ejercicio.
Si estás bifurcando un ejercicio existente, probablemente ya exista un icono para ese ejercicio. Si no, por favor abre un issue en el repositorio website-icons.