16 Jul 2013, 02:25
Dave_gnome_head_isolated_pragsmall

Dave Thomas (344 posts)

  • I’m thinking of a number between 1 and 1000…

    The most efficient way to find the number is to guess halfway between the low and high numbers of the range. If our guess is too big, then the answer lies between the bottom of the range and one less than our guess. If it is too small, then the answer lies between one more than our guess and the end of the range.

    Code this up. Your API will be guess(actual, range), where range is an Elixir range.

    Your output should look similar to:

    iex> Chop.guess(273, 1..1000)
    Is it 500
    Is it 250
    Is it 375
    Is it 312
    Is it 281
    Is it 265
    Is it 273
    273
    

    Hints:

    • You may need to implement helper functions with an additional parameter (the currently guessed number).
    • the div(a,b) function performs integer division
    • guard clauses are your friends
    • patterns can match the low and high parts of a range (a..b=4..8) === {::comment} defmodule Chop do def guess(actual, range = low..high) do guess = div(low+high, 2) IO.puts “Is it #{guess}” guess(actual, guess, range) end

    def guess(actual, actual, _), do: actual def guess(actual, guess, _low..high) when guess < actual, do: guess(actual, guess+1..high) def guess(actual, guess, low.._high) when guess > actual, do: guess(actual, low..guess-1) end {:/comment}

</yourturn>

A Possible Solution</summary>

defmodule Chop do
  def guess(actual, range = low..high) do 
    guess = div(low+high, 2)
    IO.puts "Is it #{guess}?"
    _guess(actual, guess, range)
  end

  defp _guess(actual, actual, _), 
    do: IO.puts "Yes, it's #{actual}"

  defp _guess(actual, guess,  _low..high)
    when guess < actual,
    do: guess(actual, guess+1..high)

  defp _guess(actual, guess,  low.._high) 
    when guess > actual, 
    do: guess(actual, low..guess-1)
end

Chop.guess(273, 1..1000)

</details>

01 Aug 2013, 20:32
Generic-user-small

Luke Imhoff (6 posts)

My solution:


defmodule Chop do
  def guess(actual, range) do
    n = next_guess(range)
    check_guess(actual, range, n)
  end

  defp next_guess(low..high) do
    difference = high - low
    guess = div(difference, 2) + low
    
    IO.puts "Is it #{guess}"
    
    guess
  end
  
  defp check_guess(actual, _, current_guess) when current_guess == actual do
    actual
  end
  
  defp check_guess(actual, low.._, current_guess) when current_guess > actual do
    high = current_guess - 1
    n = next_guess(low..high)
    check_guess(actual, low..high, n)
  end
  
  defp check_guess(actual, _..high, current_guess) when current_guess < actual do
    low = current_guess + 1
    n = next_guess(low..high)
    check_guess(actual, low..high, n)
  end
end ---

P.S. What mark up do I use to make a hidden thing Dave like your “A Possible Solution”? It’s not standard Markdown, so it must be something specific to this forum.

02 Aug 2013, 14:09
Dave_gnome_head_isolated_pragsmall

Dave Thomas (344 posts)

I use details/summary tags

29 Aug 2013, 12:03
Generic-user-small

Nathan J Schunemann (1 post)

I can’t figure out how to hide a code block with details/summary tags. Elixir 0.10.1

defmodule Chop do
  def guess(actual, low..high)
    when actual == div(low+high, 2) do
      IO.puts "Is it #{actual}?"
      IO.puts "Yes, it's #{actual}."
  end
 
  def guess(actual, low..high)
    when actual < div(low+high, 2) do
      IO.puts "Is it #{div(low+high, 2)}?"
      guess(actual, low..div(low+high, 2))
  end
 
  def guess(actual, low..high)
    when actual > div(low+high, 2) do
      IO.puts "Is it #{div(low+high, 2)}?"
      guess(actual, div(low+high, 2)..high)
  end
end
15 Oct 2013, 15:08
Generic-user-small

Raoul JP Bonnal (1 post)

Hi Dave, your solution uses defp which is described later in chapter 7. I figured it out only looking the solution into this forum.

29 Nov 2013, 18:02
Generic-user-small

Kit Ko (2 posts)

The syntax of defining a function that calls itself is also described later in chapter 7.

24 Dec 2013, 06:26
Snapshot_2008101_248x248_pragsmall

Rafael Rosa (2 posts)

I’m still on chapter 6, but when I looked at your proposed solution I was stunned with the matching of parameters on the right hand of the = sign (range = low..high), it’s amazing but it only works on parameter definition and I didn’t see any explanation on that so far. Granted, it isn’t necessary to answer the Chop exercise, but I would expect such explanation under “Function Calls and Pattern Matching” that starts on page 45.

As a side note, so far I’m enjoying the book very much, thanks a bunch.

05 Feb 2014, 03:34
Mva.2008.05_pragsmall

Manuel E Vidaurre Arenas (7 posts)

My solution:


defmodule Chop do
  def guess(x, a..b) when x == div(a+b, 2) do
    IO.puts "Is it #{x}"
    IO.puts "#{x}"
  end
  def guess(x, a..b) when x < div(a+b, 2) do
    n = div(a+b, 2)
    IO.puts "Is it #{n}"
    guess(x, a..n)
  end
  def guess(x, a..b) when x > div(a+b, 2) do
    n = div(a+b, 2)
    IO.puts "Is it #{n}"
    guess(x, n..b)
  end
end

It’s very similar to Nathan J Schunemann solution

26 Mar 2014, 18:01
Generic-user-small

Christian Broomfield (1 post)

My solution is virtually identical with Luke’s answer, but I didn’t use defp. Not sure if that’s important, but the program compiled just fine (0.12.5).

I tried to code a similar solution in Kotlin and ended up with this:

tailRecursive
fun guess(answer: Int, range: Range<Int>, candidate: Int = (range.end - range.start) / 2 + range.start): Int =
    when {
        answer == candidate -> candidate
        answer > candidate  -> guess(answer, (candidate + 1)..range.end)
        answer < candidate  -> guess(answer, range.start..(candidate - 1))
        else                -> throw IllegalArgumentException("Computational error occurred; check the math please.")
    }

I like this solution a lot and attempted to code this solution in Elixir:

defmodule Chop do
  def guess(answer, from..to, candidate \\ div(to - from, 2) + from) when candidate == answer do
    answer
  end

  def guess(answer, from..to, candidate \\ div(to - from, 2) + from) when candidate > answer do
    guess(answer, (candidate + 1)..to)
  end

  def guess(answer, from..to, candidate \\ div(to - from, 2) + from) when candidate < answer do
    guess(answer, from..(candidate - 1))
  end

  def guess(answer, from..to) do
    guess(answer, from..to)
  end
end

The first thing that struck me is, “There has to be an easier way” than defining four function and separating them with almost nothing but a different operator in the when clause. Secondly, the code fails with the error message:

** (CaseClauseError) no case clause matching: nil
    (elixir) src/elixir_scope.erl:22: :elixir_scope.translate_var/4
    (elixir) src/elixir_translator.erl:302: :elixir_translator.translate_arg/3
    (stdlib) lists.erl:1339: :lists.mapfoldl/3
    (elixir) src/elixir_translator.erl:209: :elixir_translator.translate/2
    (elixir) src/elixir_translator.erl:302: :elixir_translator.translate_arg/3
    (stdlib) lists.erl:1339: :lists.mapfoldl/3
    (elixir) src/elixir_translator.erl:209: :elixir_translator.translate/2
    (elixir) src/elixir_translator.erl:302: :elixir_translator.translate_arg/3
05 Jun 2014, 16:23
Me_pragsmall

Paul Barry (3 posts)

I can’t help uploading my solution for all to see, as - to be honest - I’m chuffed that it worked. :-) I’ve added a few additional messages on output to help show what’s going on:

defmodule Chop do

  def guess(target, x..y) do
    midpoint = halfway(x, y)
    match(target, x, y, midpoint)
  end

  defp halfway(x, y), do: div(y-x, 2) + x

  defp match(target, _x, _y, target), do: target

  defp match(target, x, _y, midpoint) when target < midpoint do
    IO.puts "Is it #{midpoint}?\tNo, lower."
    guess(target, x..midpoint)
  end
  
  defp match(target, _x, y, midpoint) do # in the upper range.
    IO.puts "Is it #{midpoint}?\tNo, higher."
    guess(target, midpoint..y)
  end

end

When you run my code, here’s what you see:

iex(1)> Chop.guess(273, 1..1000)
Is it 500?      No, lower.
Is it 250?      No, higher.
Is it 375?      No, lower.
Is it 312?      No, lower.
Is it 281?      No, lower.
Is it 265?      No, higher.
273
19 Jun 2014, 05:50
Generic-user-small

Rodrigo Salado Anaya (2 posts)

My solution based in the Dave T. solution.

defmodule Game do
  def guess(resp, low..high)
  when resp < low or resp > high do
    IO.puts "[error] Use a value between #{low} and #{high}."
  end

  def guess(resp, low..high) do
    guess = div(low+high, 2)
    IO.puts "Is it #{guess}"
    _find(resp, guess, low, high)
  end

  defp _find(resp, resp, _, _), do: IO.puts "-> #{resp}"

  defp _find(resp, guess, low, _)
  when guess > resp do
    guess(resp, low..guess - 1)
  end 

  defp _find(resp, guess, _, high)
  when guess < resp do
    guess(resp, guess + 1..high)
  end
end

Game.guess(20, 0..100)
Game.guess(20, 50..100)
Game.guess(50, 50..100)
Game.guess(100, 50..100)
05 Dec 2014, 21:15
9863_pragsmall

Suraj Kurapati (15 posts)

My solution is to narrow down the range of possibilities with every guess and finally converge on the actual value.

defmodule Chop do
  def guess(actual, actual.._), do: actual
  def guess(actual, _..actual), do: actual
  def guess(actual, low..high) do
    midpoint = low + div(high - low, 2)
    IO.puts "Is it #{midpoint}?"
    if actual < midpoint do
      guess actual, low..midpoint
    else
      guess actual, midpoint..high
    end
  end
end
14 Sep 2014, 09:41
Blacksad-cov_sq_pragsmall

Porco Wong (3 posts)

This is my solution:

defmodule Chop do
  def guess(actual, low..high) when actual > low and actual< high do
    _guess(actual, low..high, div(low + high, 2))
  end

  defp _guess(actual, _, curr) when curr == actual do
    IO.puts "Is it #{curr}"
    IO.puts curr
  end

  defp _guess(actual, low..high, curr) when curr < actual do
    IO.puts "Is it #{curr}"
    _guess actual, curr..high, div(curr + high, 2)
  end

  defp _guess(actual, low..high, curr) when curr > actual do
    IO.puts "Is it #{curr}"
    _guess actual, low..curr, div(low + curr, 2)
  end
end
22 Nov 2014, 01:38
Generic-user-small

lv cheng peng (4 posts)

defmodule Chop do
  def guess(num, low..high) when num > div(high + low, 2) do
    IO.puts "Is it #{div(high + low, 2)}"
    guess(num, div(high + low, 2)..high)
  end
  def guess(num, low..high) when num < div(high + low, 2) do
    IO.puts "Is it #{div(high + low, 2)}"
    guess(num, low..div(high + low, 2))
  end
  def guess(num, low..high) do
    IO.puts num
  end  
end

Chop.guess(273, 1..1000)
24 Dec 2014, 01:09
Generic-user-small

Pierre Sugar (56 posts)

And another one

 
defmodule Guess do

  def is(number, guess, _a..b) when number > guess do
    it(number, (guess+1)..b)
  end

  def is(number, guess, a.._b) when number < guess do
    it(number, a..(guess-1))
  end

  def is(number, guess, _a.._b) when number == guess do
    IO.puts guess
  end

  def it(number, a..b = range) when a <= number and b >= number do
    guess = div(b-a,2) + a
    IO.puts "Is it #{guess}?"
    is(number, guess, range)
  end
end

25 Dec 2014, 08:32
Generic-user-small

Cifer Y (3 posts)

defmodule Chop do
  def guess(num, range) do
    guess(num, range, 0)
  end

  defp guess(num, range, guess) when num == guess do
    IO.puts "It is #{guess}!"
  end

  defp guess(num, range, guess) when guess > num do
    min.._ = range
    new_range = min..guess
    next_guess = mid(new_range)
    IO.puts "Is it #{next_guess}?"
    guess(num, new_range, next_guess)
  end

  defp guess(num, range, guess) when guess < num do
    _..max = range
    new_range = guess..max
    next_guess = mid(new_range)
    IO.puts "Is it #{next_guess}?"
    guess(num, new_range, next_guess)
  end

  defp mid(range) do
    min..max = range
    mid = div(min + max, 2)
    mid
  end
end

17 Jan 2015, 04:09
Generic-user-small

Anonymous (1 post)

defmodule Chop do
  def guess(actual, _, actual), do: IO.puts "Yes, it's #{actual}"
  def guess(actual, low.._, guess) when guess > actual, do: guess(actual, low..guess-1)
  def guess(actual, _..high, guess) when guess < actual, do: guess(actual, guess+1..high)
  def guess(actual, low..high) do
    guess = div(low+high, 2)
    IO.puts "Is it #{guess}?"
    guess(actual, low..high, guess)
  end
end
22 Feb 2015, 12:11
Generic-user-small

Abdulaziz AlShetwi (1 post)

defmodule Chop do
  def guess actual, range = low..high do
    current = div(high+low, 2)
    IO.puts "Is it #{current}"
    isit(current, actual, range)
  end
  
  def isit(current, actual, low.._high )
  when current > actual do
    guess(actual, low..current-1)
  end

  def isit(current, actual, _low..high)
  when current < actual do
    guess(actual, current+1..high)
  end

  # borrow this idea from Mr. Dave .. this is so clever :)
  def isit(actual, actual, _) do
   IO.puts "Yes it is #{actual}"
  end
  
end

Chop.guess 273, 1..1000
28 Mar 2015, 18:47
Generic-user-small

philippe tseyen (4 posts)

defmodule Chop do
  def evaluate(actual, guess, _) when guess == actual do
    IO.puts "Is it #{guess}"
    guess
  end

  def evaluate(actual, guess, a..b) when guess < actual do
    IO.puts "Is it #{guess}"
    new_range = guess..b
    new_guess = mid(new_range)
    evaluate(actual, new_guess, new_range)
  end

  def evaluate(actual, guess, a..b) when guess > actual do
    IO.puts "Is it #{guess}"
    new_range = a..guess
    new_guess = mid(new_range)
    evaluate(actual, new_guess, a..guess)
  end

  def guess(actual, a..b) do
    first_guess = mid(a..b)
    evaluate(actual, first_guess, a..b)
  end

  def mid(a..b) do
    div(a+b,2)
  end
end
10 Jul 2015, 12:51
Generic-user-small

Michael Anthony (2 posts)

I think I might have messed up by using cond do since no one else did. It’s so concise though…

defmodule Chop do
  def guess(actual, h..t) do
    g = div((t - h), 2) + h
    IO.puts "Is it #{g}"
    cond do
      g == actual -> actual
      g < actual -> guess(actual, g + 1..t)
      g > actual -> guess(actual, h..g - 1)
    end
  end
end
iex> Chop.guess 783, 1..1000
Is it 500
Is it 750
Is it 875
Is it 812
Is it 781
Is it 796
Is it 788
Is it 784
Is it 782
Is it 783
783
16 Jul 2015, 04:54
Generic-user-small

Felipe Juarez Murillo (5 posts)

My solution is not that elegant.. but still works but definitely I should swtich to Nathan J Schunemann solution

defmodule Chop do
  def guess(x, min..max) do
    guessing(x, min, max)
  end
  
  def guessing(x, min, max) when x < div(max+min, 2) do
    IO.puts "Is it #{div(max + min, 2)}"
    guessing(x, min, div(max + min, 2))
  end
  
  def guessing(x, min, max) when x > div(max+min, 2) do
    IO.puts "Is it #{div(max + min, 2)}"
    guessing(x, div(max + min, 2), max)
  end
  
  def guessing(x, min, max) when x == div(max+min, 2) do
    IO.puts "Is it #{div(max + min, 2)}"
    IO.puts "#{div(max + min, 2)}"
  end
end
22 Jul 2015, 18:46
Generic-user-small

Srikanth P Shreenivas (1 post)

My first version was close to what Dave has proposed.

defmodule Chop do
	def guess(m, a..b) do		
		check_guess(div(a+b,2), m, a..b)
	end
	def check_guess(g, m, _a.._b) when m == g do
		IO.puts "It is #{g}"
	end
	def check_guess(g, m, _a..b) when m > g do
		IO.puts "Is it #{g}?"
		guess(m, g..b)
	end
	def check_guess(g, m, a.._b) when m < g do
		IO.puts "Is it #{g}?"
		guess(m, a..g)
	end
end

Chop.guess(273, 1..1000)
  You must be logged in to comment