Skip to content

2. Extended Examples

1. Example 01

You are designing a system that monitors and regulates the turbine inlet temperature, oil temperature, and oil pressure for an aircraft engine. Every cycle a new reading is taken and appended to a list such as the following:

[851, 841, 800, 756, 640, 390, 201] … where the head of the list is the latest reading taken. The componet you are building is tasked in monitoring for anomalous spikes or drops that may indicate mechanical failure.

You may assume all readings are integers.

Q1: Latest Reading

Implement EngineMonitor.latest/1 such that it returns the last reading taken from a given list.

Here’s how you can implement EngineMonitor.latest/1 in Elixir to return the last reading taken from a given list:

defmodule EngineMonitor do
def latest([latest | _rest]), do: latest
end

Given a list, this function extracts and returns the head, which is the latest reading.

For example:

readings = [851, 841, 800, 756, 640, 390, 201]
EngineMonitor.latest(readings) # returns 851

Q2: Max Reading

EngineMonitor.max_reading/1 returns the maximum reading from a list:

defmodule EngineMonitor do
def max_reading(readings) do
Enum.max(readings)
end
end
  • Enum.max/1 finds the maximum value in the readings list.
  • The function returns the highest integer from the list.

Example Usage:

readings = [851, 841, 800, 756, 640, 390, 201]
EngineMonitor.max_reading(readings) # Returns 851

- Using Recursion:

To implement EngineMonitor.max_reading/1 without using Enum, you can use recursion to find the maximum value in a list. Here’s how you can do it:

defmodule EngineMonitor do
def max_reading([head | tail]) do
max_reading(tail, head)
end
defp max_reading([], max), do: max
defp max_reading([head | tail], max) do
new_max = if head > max, do: head, else: max
max_reading(tail, new_max)
end
end

1. max_reading([head | tail]): Starts the recursion with the first element of the list as the initial maximum value. 2. max_reading([], max): Base case, returns the maximum value when the list is empty. 3. max_reading([head | tail], max): Recursive case, updates the maximum value if the current element is greater, then proceeds with the rest of the list.

Example Usage:

readings = [851, 841, 800, 756, 640, 390, 201]
EngineMonitor.max_reading(readings) # Returns 851

Q3: Rising Reading

EngineMonitor.rising/1 returns the number of times a reading has risen in a given list, you can compare each element with the next one and count how many times the reading increases.

defmodule EngineMonitor do
def rising(readings) do
readings
|> Enum.chunk_every(2, 1, :discard)
|> Enum.count(fn [a, b] -> b > a end)
end
end
  1. Enum.chunk_every(2, 1, :discard): Splits the list into overlapping pairs.
  2. Enum.count(fn [a, b] -> b > a end): Counts how many pairs have a rising trend.

Example Usage:

readings = [851, 841, 800, 756, 640, 390, 201]
EngineMonitor.rising(readings) # Returns 0
readings = [200, 300, 250, 400, 450]
EngineMonitor.rising(readings) # Returns 3

This implementation counts how many times a reading has increased compared to the previous one.

Q4: Danger Reading

EngineMonitor.danger?/1 returns true if a dangerous spike or drop in readings (a change of 50 or more) is detected, you can compare consecutive readings and check if the difference between them is 50 or greater.

defmodule EngineMonitor do
def danger?(readings) do
readings
|> Enum.chunk_every(2, 1, :discard)
|> Enum.any?(fn [a, b] -> abs(a - b) >= 50 end)
end
end
  1. Enum.chunk_every(2, 1, :discard): Creates overlapping pairs of consecutive readings.
  2. Enum.any?(fn [a, b] -> abs(a - b) >= 50 end): Checks if any pair has an absolute difference of 50 or more.

Example Usage:

readings = [851, 841, 800, 756, 640, 390, 201]
EngineMonitor.danger?(readings) # Returns true (640 -> 390 is a drop of 250)
readings = [200, 220, 230, 240, 245]
EngineMonitor.danger?(readings) # Returns false (no spike or drop of 50 or more)

This implementation correctly identifies hazardous conditions based on the defined threshold.

- Using Recursion

To implement EngineMonitor.danger?/1 without using Enum, you can use recursion to check for dangerous spikes or drops. Here’s how you can do it:

defmodule EngineMonitor do
def danger?(readings) do
check_danger(readings, nil)
end
defp check_danger([], _previous), do: false
defp check_danger([current | rest], nil) do
check_danger(rest, current)
end
defp check_danger([current | rest], previous) do
if abs(current - previous) >= 50 do
true
else
check_danger(rest, current)
end
end
end

1. danger?(readings): Starts the recursion by calling check_danger/2 with an initial value of nil for the previous reading. 2. check_danger([], _previous): Base case, returns false when the list is empty (no dangerous changes found). 3. check_danger([current | rest], nil): When starting with the first element, there’s no previous reading to compare to. Continue with the rest of the list. 4. check_danger([current | rest], previous): Compares the current reading with the previous one. If the absolute difference is 50 or more, return true. Otherwise, continue checking the rest of the list.

Example Usage:

readings = [851, 841, 800, 756, 640, 390, 201]
EngineMonitor.danger?(readings) # Returns true (640 -> 390 is a drop of 250)
readings = [200, 220, 230, 240, 245]
EngineMonitor.danger?(readings) # Returns false (no spike or drop of 50 or more)

This implementation uses recursion to traverse the list and identify any dangerous changes based on the specified threshold.

Q5: Fire Reading

EngineMonitor.fire?/1 detects whether the word “FIRE” appears in a string regardless of case, you can use String.downcase/1 to convert the string to lowercase and then check for the presence of the lowercase equivalent of “FIRE”.

defmodule EngineMonitor do
def fire?(text) do
String.contains?(String.downcase(text), "fire")
end
end

1. String.downcase(text): Converts the entire input string to lowercase. 2. String.contains?(string, "fire"): Checks if the lowercase string contains the substring "fire".

Example Usage:

EngineMonitor.fire?("The alarm system detected a FIRE") # Returns true
EngineMonitor.fire?("No fire detected") # Returns true
EngineMonitor.fire?("Just a test") # Returns false

This implementation ensures that the check for the word “FIRE” is case-insensitive.