Overview
Control structures determine the order in which statements execute in a program. Without control structures, code executes sequentially from top to bottom. Control structures break this linear flow by introducing decision points, repetition, and branching based on runtime conditions.
Three primary categories exist: sequential execution (default behavior), selection structures (conditional branching), and iteration structures (loops). Selection structures evaluate conditions and execute different code paths based on boolean results. Iteration structures repeat code blocks until termination conditions are met. Jump statements transfer control to different program locations.
Modern programming languages implement control structures through keywords and syntax that compile to machine-level branch and jump instructions. The processor evaluates conditions and modifies the instruction pointer based on results. Ruby inherits control structure concepts from languages like Smalltalk and Perl, adding syntax designed for readability.
# Sequential execution - no control structures
x = 10
y = 20
z = x + y
# Selection structure - conditional branching
if z > 25
puts "Sum exceeds threshold"
end
# Iteration structure - repeated execution
3.times do |i|
puts "Iteration #{i}"
end
# => Iteration 0
# => Iteration 1
# => Iteration 2
Control structures interact with variable scope, exception handling, and method returns. The structure chosen affects code readability, performance characteristics, and maintainability. Different structures suit different problem patterns.
Key Principles
Control structures manipulate program flow through three fundamental mechanisms: evaluation, branching, and repetition. Evaluation determines truth values from expressions. Branching selects execution paths based on evaluation results. Repetition executes code blocks multiple times according to specified conditions.
Conditional Evaluation
Expressions evaluate to boolean values that determine which code path executes. Ruby treats specific values as falsy (false and nil) while all other values are truthy. This differs from languages where zero, empty strings, or empty collections are falsy. Evaluation occurs at runtime and can include method calls, variable comparisons, and complex boolean logic.
Control Flow Transfer
Control flow transfer moves execution from the current statement to a different location in the program. Unconditional transfers occur through statements like return, break, and next. Conditional transfers depend on boolean evaluation results. Each transfer mechanism has specific scope rules and affects the call stack differently.
Branching Logic
Branching creates multiple execution paths within a program. Simple branching involves binary decisions (if/else). Complex branching handles multiple conditions (case/when statements). Branching depth affects code complexity - nested structures create multiple decision points that compound the possible execution paths.
Loop Constructs
Loops repeat code execution while maintaining state across iterations. Pre-test loops evaluate conditions before each iteration. Post-test loops guarantee at least one execution. Infinite loops continue until explicit termination. Each iteration can modify loop variables, access external state, or perform side effects.
Short-Circuit Evaluation
Boolean operators employ short-circuit evaluation where the second operand is not evaluated if the result can be determined from the first operand alone. The && operator returns the first falsy value or the last value if all are truthy. The || operator returns the first truthy value or the last value if all are falsy.
# Short-circuit with &&
result = nil && expensive_operation() # expensive_operation never called
# => nil
# Short-circuit with ||
value = user_input || default_value # default_value used only if user_input is falsy
Guard Clauses
Guard clauses handle edge cases and invalid conditions at the start of methods or blocks. Early returns prevent deep nesting and improve readability by eliminating invalid states before main logic executes. Guard clauses make the expected execution path more prominent by handling exceptional cases first.
Expression-Oriented Control
Ruby treats control structures as expressions that return values. Every control structure evaluates to the last expression executed within it. This enables assignment from conditionals, inline ternary operations, and functional programming patterns where control flow produces values rather than just causing side effects.
# Control structure as expression
status = if score > 90
"excellent"
elsif score > 70
"good"
else
"needs improvement"
end
Ruby Implementation
Ruby implements control structures with syntax prioritizing readability and expression-oriented programming. The language provides multiple ways to express the same control flow, each with different readability and performance characteristics.
Conditional Statements
The if statement evaluates a condition and executes a code block when true. Ruby supports elsif for additional conditions and else for default cases. Statement modifiers allow single-line conditionals by placing the if keyword after the statement.
# Multi-line if statement
if temperature > 30
puts "Hot"
elsif temperature > 20
puts "Warm"
else
puts "Cold"
end
# Statement modifier - executes only if condition true
puts "Freezing" if temperature < 0
# Unless - inverted conditional logic
unless authenticated?
redirect_to login_path
end
# Unless statement modifier
process_data unless cache_exists?
The unless keyword provides inverted conditional logic - the block executes when the condition is false. This improves readability when expressing negative conditions but should not be combined with elsif or complex boolean expressions.
Ternary Operator
The ternary operator condition ? true_value : false_value provides inline conditional expressions. Use for simple value selection where both branches return values without side effects.
# Ternary for value assignment
display_name = username.nil? ? "Anonymous" : username
# Avoid ternary for side effects - use regular if instead
# Bad
condition ? execute_action() : execute_other_action()
# Good
if condition
execute_action()
else
execute_other_action()
end
Case Statements
Case statements match a value against multiple patterns. Ruby's case/when evaluates each when clause using the === operator, which enables pattern matching beyond simple equality.
# Basic case statement
case status_code
when 200
"Success"
when 404
"Not Found"
when 500..599
"Server Error"
else
"Unknown Status"
end
# Pattern matching with ranges and types
case input
when String
input.upcase
when Integer
input * 2
when 1..10
"Small number"
else
nil
end
# Multiple values per when clause
case day
when "Saturday", "Sunday"
"Weekend"
when "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
"Weekday"
end
Loop Constructs
Ruby provides several loop mechanisms with different use cases. The while loop continues while a condition remains true. The until loop continues while a condition remains false. The loop method creates an infinite loop terminated by explicit break statements.
# While loop - pre-test condition
counter = 0
while counter < 5
puts counter
counter += 1
end
# Until loop - inverted while logic
attempts = 0
until connection_established? || attempts > 3
establish_connection()
attempts += 1
end
# Infinite loop with explicit break
loop do
data = fetch_next_item()
break if data.nil?
process(data)
end
Iterator Methods
Ruby emphasizes iterator methods over traditional loops. Methods like each, times, upto, and downto provide functional iteration with block parameters.
# Array iteration
[1, 2, 3].each do |num|
puts num * 2
end
# Numeric iteration
5.times { |i| puts "Iteration #{i}" }
# Range iteration
1.upto(10) do |n|
puts n if n.even?
end
# Infinite enumeration with lazy evaluation
(1..Float::INFINITY).lazy.select(&:even?).first(5)
# => [2, 4, 6, 8, 10]
Loop Control Keywords
The break keyword exits the innermost loop immediately. The next keyword skips to the next iteration. The redo keyword restarts the current iteration without re-evaluating the loop condition. The retry keyword restarts the entire loop from the beginning.
# Break - exit loop early
result = [1, 2, 3, 4, 5].each do |num|
break num if num > 3
end
# => 4
# Next - skip to next iteration
[1, 2, 3, 4, 5].each do |num|
next if num.even?
puts num
end
# => Prints 1, 3, 5
# Redo - restart current iteration
attempts = 0
5.times do |i|
attempts += 1
puts "Attempt #{attempts} for iteration #{i}"
redo if attempts < 2 && i == 2
end
Boolean Operators
Ruby provides both logical operators (&&, ||, !) and keyword operators (and, or, not). The symbolic operators have higher precedence than assignment, while keyword operators have lower precedence. This precedence difference creates different evaluation orders.
# Symbolic operators - higher precedence
result = true && false
# result = false
# Keyword operators - lower precedence
result = true and false
# result = true (assignment happens first, then 'and' evaluates)
# Using || for default values
name = user.name || "Unknown"
# Using && for nil-safe method calls
length = string && string.length
Practical Examples
Configuration Validation
Control structures validate configuration objects before system initialization. Multiple conditions check required fields, type constraints, and cross-field dependencies.
class ConfigurationValidator
def validate(config)
# Guard clauses for required fields
return [:error, "Missing database host"] if config[:db_host].nil?
return [:error, "Missing API key"] if config[:api_key].nil?
# Type validation
unless config[:port].is_a?(Integer)
return [:error, "Port must be integer"]
end
# Range validation
if config[:port] < 1024 || config[:port] > 65535
return [:error, "Port out of valid range"]
end
# Conditional requirement
if config[:ssl_enabled] && config[:ssl_cert].nil?
return [:error, "SSL certificate required when SSL enabled"]
end
[:ok, "Configuration valid"]
end
end
validator = ConfigurationValidator.new
status, message = validator.validate({
db_host: "localhost",
api_key: "secret",
port: 5432,
ssl_enabled: true,
ssl_cert: "/path/to/cert"
})
Data Processing Pipeline
Loops process collections of data with conditional logic determining how each item is handled. Breaking, skipping, and accumulating results based on item properties.
class DataProcessor
def process_records(records)
results = []
error_count = 0
records.each_with_index do |record, index|
# Skip invalid records
next unless record[:id] && record[:data]
# Break on too many errors
if error_count > 10
puts "Error threshold exceeded at record #{index}"
break
end
# Conditional processing based on record type
processed = case record[:type]
when :numeric
process_numeric(record[:data])
when :text
process_text(record[:data])
when :binary
process_binary(record[:data])
else
error_count += 1
next
end
# Conditional result storage
results << processed if processed[:status] == :success
end
{
processed: results.size,
errors: error_count,
results: results
}
end
private
def process_numeric(data)
value = data.to_f
{ status: :success, value: value, squared: value ** 2 }
rescue
{ status: :error }
end
def process_text(data)
return { status: :error } if data.empty?
{ status: :success, length: data.length, upcase: data.upcase }
end
def process_binary(data)
{ status: :success, size: data.bytesize }
end
end
State Machine Implementation
Control structures implement finite state machines where state transitions depend on current state and input events.
class OrderStateMachine
STATES = [:pending, :processing, :shipped, :delivered, :cancelled]
def initialize
@state = :pending
@history = []
end
def transition(event)
@history << [@state, event, Time.now]
case @state
when :pending
case event
when :pay
@state = :processing
when :cancel
@state = :cancelled
else
raise "Invalid transition from pending: #{event}"
end
when :processing
case event
when :ship
@state = :shipped
when :cancel
# Only allow cancel if processing time under 1 hour
if processing_time < 3600
@state = :cancelled
else
raise "Cannot cancel after processing began"
end
else
raise "Invalid transition from processing: #{event}"
end
when :shipped
if event == :deliver
@state = :delivered
else
raise "Invalid transition from shipped: #{event}"
end
when :delivered, :cancelled
raise "Order in terminal state: #{@state}"
end
@state
end
def processing_time
processing_entry = @history.find { |state, _, _| state == :processing }
return 0 unless processing_entry
Time.now - processing_entry[2]
end
attr_reader :state, :history
end
# Usage
order = OrderStateMachine.new
order.transition(:pay) # => :processing
order.transition(:ship) # => :shipped
order.transition(:deliver) # => :delivered
Retry Logic with Backoff
Loops implement retry mechanisms with exponential backoff for transient failures.
class APIClient
MAX_RETRIES = 5
BASE_DELAY = 1
def fetch_with_retry(url)
attempt = 0
loop do
response = fetch(url)
# Success - return immediately
return response if response.status == 200
# Client error - don't retry
if response.status >= 400 && response.status < 500
raise "Client error: #{response.status}"
end
# Server error - retry with backoff
attempt += 1
if attempt >= MAX_RETRIES
raise "Max retries exceeded for #{url}"
end
# Exponential backoff
delay = BASE_DELAY * (2 ** attempt)
# Jitter to prevent thundering herd
delay += rand(0..delay / 2)
puts "Attempt #{attempt} failed, waiting #{delay}s"
sleep(delay)
# Retry continues from top of loop
end
end
private
def fetch(url)
# Simulated HTTP request
# Returns mock response with random status
OpenStruct.new(status: [200, 500, 503].sample)
end
end
Common Patterns
Guard Clause Pattern
Guard clauses eliminate nested conditionals by handling edge cases and invalid states at method entry. Early returns make the main execution path more prominent.
# Without guard clauses - nested conditions
def process_order(order)
if order
if order.items.any?
if order.customer
if order.customer.active?
# Main logic buried deep in nesting
calculate_total(order)
end
end
end
end
end
# With guard clauses - flat structure
def process_order(order)
return nil if order.nil?
return nil if order.items.empty?
return nil if order.customer.nil?
return nil unless order.customer.active?
# Main logic prominent at method end
calculate_total(order)
end
Null Object Pattern
Replace conditional nil checks with objects that respond to the same interface but provide safe default behavior.
class NullUser
def name
"Guest"
end
def permissions
[]
end
def admin?
false
end
end
# Without null object
def display_user(user)
if user
puts "Welcome, #{user.name}"
puts "Admin: #{user.admin?}"
else
puts "Welcome, Guest"
puts "Admin: false"
end
end
# With null object
def display_user(user)
user ||= NullUser.new
puts "Welcome, #{user.name}"
puts "Admin: #{user.admin?}"
end
Strategy Pattern
Replace conditional logic with polymorphic objects implementing a common interface.
# Conditional approach
def calculate_shipping(order, method)
case method
when :standard
order.weight * 0.5
when :express
order.weight * 1.5 + 10
when :overnight
order.weight * 3.0 + 25
end
end
# Strategy pattern
class StandardShipping
def calculate(order)
order.weight * 0.5
end
end
class ExpressShipping
def calculate(order)
order.weight * 1.5 + 10
end
end
class OvernightShipping
def calculate(order)
order.weight * 3.0 + 25
end
end
def calculate_shipping(order, strategy)
strategy.calculate(order)
end
# Usage
order = OpenStruct.new(weight: 10)
cost = calculate_shipping(order, ExpressShipping.new)
Loop Accumulation Pattern
Iterate through collections while accumulating results, often with conditional inclusion.
# Manual accumulation
def extract_valid_emails(users)
emails = []
users.each do |user|
next unless user.email
next unless user.email.include?('@')
next if user.suspended?
emails << user.email
end
emails
end
# Using select and map
def extract_valid_emails(users)
users
.reject(&:suspended?)
.map(&:email)
.compact
.select { |email| email.include?('@') }
end
State Validation Pattern
Chain multiple validations with early exit on first failure.
class Validator
def self.validate(data)
return error("Data is nil") if data.nil?
return error("Data is empty") if data.empty?
return error("Missing required field") unless data[:required_field]
return error("Invalid format") unless valid_format?(data)
return error("Failed business rule") unless business_rule_check(data)
success(data)
end
def self.error(message)
{ valid: false, error: message }
end
def self.success(data)
{ valid: true, data: data }
end
end
Performance Considerations
Branch Prediction
Modern processors predict branch outcomes to optimize instruction pipelining. Predictable branches execute faster than random branches. Conditional patterns that consistently evaluate the same way benefit from branch prediction.
# Predictable branch - processor learns pattern
1000.times do |i|
if i.even? # Alternating pattern
process_even(i)
else
process_odd(i)
end
end
# Unpredictable branch - random data
random_data.each do |value|
if meets_complex_condition?(value) # Random true/false
process_a(value)
else
process_b(value)
end
end
Short-Circuit Optimization
Place expensive operations last in boolean chains. Short-circuit evaluation prevents unnecessary computation when early conditions determine the result.
# Inefficient - expensive check happens first
if expensive_database_query() && simple_check()
execute_action()
end
# Efficient - simple check first
if simple_check() && expensive_database_query()
execute_action()
end
# Multiple conditions ordered by cost
if cached_value() || medium_cost_check() || expensive_operation()
proceed()
end
Loop Optimization
Extract invariant calculations outside loops to prevent redundant computation. Minimize method calls and object allocations within tight loops.
# Inefficient - repeated calculation
array.each do |item|
threshold = calculate_threshold() # Calculated every iteration
process(item) if item > threshold
end
# Efficient - calculate once
threshold = calculate_threshold()
array.each do |item|
process(item) if item > threshold
end
# Inefficient - method calls in loop
data.each do |item|
processor.setup() # Setup every iteration
processor.process(item)
processor.cleanup()
end
# Efficient - setup once
processor.setup()
data.each do |item|
processor.process(item)
end
processor.cleanup()
Iterator Method Performance
Different iterator methods have different performance characteristics. each typically performs better than for in Ruby. Block allocation impacts performance for tight loops with many iterations.
require 'benchmark'
data = (1..1000000).to_a
Benchmark.bm do |x|
x.report("each") { data.each { |n| n * 2 } }
x.report("for") { for n in data; n * 2; end }
x.report("while") {
i = 0
while i < data.length
data[i] * 2
i += 1
end
}
end
# Typical results show 'each' and 'while' similar, 'for' slightly slower
Case Statement Optimization
Case statements evaluate conditions sequentially. Place most frequent conditions first to minimize average comparison count.
# Inefficient - rare cases checked first
case status_code
when 418 # I'm a teapot - extremely rare
handle_teapot()
when 200 # Success - most common
handle_success()
end
# Efficient - common cases first
case status_code
when 200 # Most common checked first
handle_success()
when 404
handle_not_found()
when 418 # Rare case checked last
handle_teapot()
end
Avoiding Redundant Conditionals
Store conditional results in variables when the same condition is checked multiple times.
# Redundant evaluation
if expensive_check(data)
log("Check passed")
end
if expensive_check(data) # Evaluated again
process(data)
end
# Single evaluation
check_result = expensive_check(data)
if check_result
log("Check passed")
end
if check_result
process(data)
end
Common Pitfalls
Assignment in Conditionals
Ruby allows assignment within conditional expressions. Single = performs assignment while == checks equality. Assignment in conditionals returns the assigned value, which is then evaluated for truthiness.
# Accidental assignment
if user = nil # Assigns nil to user, evaluates to nil (falsy)
puts "This never executes"
end
# Intended equality check
if user == nil
puts "User is nil"
end
# Common mistake with input validation
if input = "" # Assigns empty string, evaluates to "" (truthy)
puts "This executes even though string is empty"
end
Elsif Misspelling
Ruby uses elsif, not elseif or else if. Misspelling creates syntax errors.
# Wrong
if x > 10
"high"
else if x > 5 # Syntax error
"medium"
end
# Correct
if x > 10
"high"
elsif x > 5
"medium"
end
Unless with Else
Combining unless with else creates confusing logic. The else clause executes when the condition is true, inverting intuitive understanding.
# Confusing - unless with else
unless user.admin?
redirect_to :home
else
show_admin_panel # Executes when user IS admin
end
# Clear - use if with negation
if user.admin?
show_admin_panel
else
redirect_to :home
end
Modifier Statement Scope
Statement modifiers apply only to the statement immediately before them, not to multiple statements.
# Wrong - only puts executes conditionally
puts "Processing"
process_data() if condition # Only process_data is conditional
# Correct - block for multiple statements
if condition
puts "Processing"
process_data()
end
Break Return Values
The break keyword can take an argument that becomes the return value of the loop or iterator method. Forgetting this causes unexpected nil returns.
# Returns nil
result = [1, 2, 3].each do |n|
break if n > 2
end
# => nil
# Returns the value
result = [1, 2, 3].each do |n|
break n if n > 2
end
# => 3
Case Statement Fall-Through
Ruby case statements do not fall through. Each when clause exits the case statement when matched. Multiple conditions in one when require comma separation.
# Wrong - syntax error
case value
when 1
when 2 # This is a syntax error, not fall-through
puts "One or two"
end
# Correct - multiple values in one when
case value
when 1, 2
puts "One or two"
end
Infinite Loop Creation
Loop conditions that never become false create infinite loops. Common causes include missing increment statements or incorrect condition logic.
# Infinite loop - counter never increments
counter = 0
while counter < 10
puts counter
# Missing: counter += 1
end
# Infinite loop - condition never changes
while !file.exists?
process_data()
# Missing: code to create file or break condition
end
# Correct - explicit break condition
timeout = Time.now + 30
while !file.exists?
process_data()
break if Time.now > timeout
end
Short-Circuit Side Effects
Relying on side effects in short-circuited expressions causes inconsistent behavior when the expression short-circuits.
# Dangerous - second call may not execute
if check_condition() && update_counter()
proceed()
end
# Safe - execute side effects separately
check = check_condition()
update_counter()
if check
proceed()
end
Loop Variable Mutation
Modifying loop variables inside the loop body causes unexpected iteration counts or skipped elements.
# Wrong - modifying loop array during iteration
array = [1, 2, 3, 4, 5]
array.each do |item|
array.delete(item) if item.even? # Modifies array being iterated
end
# => Results in skipped elements
# Correct - iterate over copy or build new array
array = [1, 2, 3, 4, 5]
array = array.reject(&:even?) # Creates new array
Reference
Control Structure Keywords
| Keyword | Purpose | Usage Context |
|---|---|---|
| if | Conditional execution | Executes block when condition true |
| elsif | Additional condition | Checked when previous conditions false |
| else | Default case | Executes when all conditions false |
| unless | Inverted conditional | Executes block when condition false |
| case | Pattern matching | Matches value against multiple patterns |
| when | Case clause | Defines pattern to match in case statement |
| while | Pre-test loop | Continues while condition true |
| until | Inverted loop | Continues while condition false |
| for | Collection iteration | Iterates over enumerable object |
| loop | Infinite loop | Continues until explicit break |
| break | Exit loop | Immediately exits innermost loop |
| next | Skip iteration | Moves to next iteration of loop |
| redo | Restart iteration | Restarts current iteration without re-evaluating |
| retry | Restart loop | Restarts entire loop from beginning |
| return | Method exit | Exits method and returns value |
Iterator Methods
| Method | Description | Return Value |
|---|---|---|
| each | Iterate with block | Original collection |
| times | Execute block n times | Number of iterations |
| upto | Iterate from value up to limit | Starting value |
| downto | Iterate from value down to limit | Starting value |
| loop | Infinite iteration | nil or break value |
| each_with_index | Iterate with index parameter | Original collection |
| map | Transform each element | New array of transformed values |
| select | Filter elements | New array of matching elements |
| reject | Exclude elements | New array of non-matching elements |
| find | Locate first match | First matching element or nil |
| reduce | Accumulate result | Accumulated value |
Boolean Operators
| Operator | Symbolic | Keyword | Short-Circuit | Precedence |
|---|---|---|---|---|
| AND | && | and | Yes | Higher (symbolic) / Lower (keyword) |
| OR | || | or | Yes | Higher (symbolic) / Lower (keyword) |
| NOT | ! | not | No | Higher (symbolic) / Lower (keyword) |
Truthiness Rules
| Value Type | Truthy | Falsy |
|---|---|---|
| Boolean | true | false |
| Nil | nil | |
| Zero | 0 | |
| Empty String | "" | |
| Empty Array | [] | |
| Empty Hash | {} |
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
| == | Equal value | 5 == 5 |
| != | Not equal | 5 != 3 |
| < | Less than | 3 < 5 |
| > | Greater than | 5 > 3 |
| <= | Less than or equal | 3 <= 5 |
| >= | Greater than or equal | 5 >= 3 |
| <=> | Comparison (spaceship) | 5 <=> 3 returns 1 |
| === | Case equality | String === "hello" |
| =~ | Pattern match | "hello" =~ /ell/ |
Statement Modifiers
Statement modifiers allow single-line conditional execution by placing the keyword after the statement.
| Modifier | Syntax | Execution Condition |
|---|---|---|
| if | statement if condition | When condition true |
| unless | statement unless condition | When condition false |
| while | begin statement end while condition | After first execution while condition true |
| until | begin statement end until condition | After first execution until condition true |
Loop Control Effects
| Keyword | Affects | Continues | Returns |
|---|---|---|---|
| break | Innermost loop | No | Optional value |
| next | Current iteration | Yes (next iteration) | nil |
| redo | Current iteration | Yes (same iteration) | nil |
| retry | Entire loop | Yes (from start) | nil |
| return | Method | No | Optional value |
Case Statement Patterns
# Equality matching
case value
when 1 then "one"
when 2 then "two"
end
# Range matching
case age
when 0..12 then "child"
when 13..19 then "teen"
else "adult"
end
# Class matching
case object
when String then object.upcase
when Integer then object * 2
end
# Regex matching
case input
when /^[A-Z]/ then "starts with capital"
when /\d+/ then "contains digits"
end
# Multiple values
case status
when :pending, :processing then "in progress"
when :complete then "done"
end
Performance Comparison
| Structure | Use Case | Performance | Readability |
|---|---|---|---|
| if/elsif | Few conditions (2-3) | Good | Excellent |
| case/when | Many conditions (4+) | Better | Excellent |
| Hash lookup | Static mappings | Best | Good |
| Polymorphism | Complex behavior | Good | Excellent (for objects) |
| Ternary | Simple value selection | Best | Good (when simple) |
Conditional Expression Return Values
All control structures in Ruby return values based on the last evaluated expression.
# if returns last evaluated expression
result = if x > 10
"high" # Returned if condition true
else
"low" # Returned if condition false
end
# case returns matched branch value
category = case score
when 90..100 then "A"
when 80..89 then "B"
else "F"
end
# Loop with break returns break value
first_match = array.each do |item|
break item if item > 10
end