how do I define the [] method for accessing class members in a ruby class? - ruby-1.9.3

I have written a class in ruby and I wish to define the method [] to access its members (all members are public). For example, for the following simple class:
class Boy
attr_accessor :name, :age
def initialize(n, a)
#name = n
#age = a
end
end
I wish the following to be the same
b = Boy.new(Tom, 23)
b.name # Tom
b[:name] # Tom
Any help please? Thanks in advance

class Boy
attr_accessor :name, :age
def initialize(n, a)
#name = n
#age = a
end
def [](key)
instance_variable_get(key)
end
end
b = Boy.new("Tom", 23)
b.name # Tom
b[:#name] # Tom
Or
class Boy
attr_accessor :name, :age
def initialize(n, a)
#name = n
#age = a
end
def [](key)
public_send(key)
end
end
b = Boy.new("Tom", 23)
b.name # Tom
b[:name] # Tom
And if you want to define []= method,
For the first style:
def []=(key, value)
instance_variable_set(key, value)
end
For the second style:
def []=(key, value)
public_send(key.to_s + '=', value)
end

Related

How to create custom operators with precedence

I hava a class with custom Operators
case class Num(var value:Int) {
def add(x:Num) = Num(value + x.value)
def mul(x:Num) = Num(value * x.value)
}
So I can call them like this
val a = Num(2)
val b = Num(3)
val c = Num(4)
val m1 = a add b mul c
But how can I execute mul before add? I saw a solution like +| instead of add, but I want include letters in my Operator and +add and *mul not working. Also I want to include a pow function, so this needs an higher precidence than mul
You can use Parenthesis after add.
val m1 = a add (b mul c) = 14
val m1 = a add b mul c = 20
Update
you do not have any restrictions in naming your methods. For example, you can define methods +, -, * and etc. for a class.
case class Num(var value:Int) {
def + (x:Num) = Num(value + x.value)
def *(x:Num) = Num(value * x.value)
}
object Num extends App {
val a = Num(2)
val b = Num(3)
val c = Num(4)
val m1 = a + b * c
println(m1)
}
Output
Num(14)

How to dynamically create modules with functions

On compilation stage I can easily produce functions with:
defmodule A1 do
defmodule A2 do
Enum.each %{m: 42}, fn {k, v} ->
def unquote(k)(), do: unquote(v)
end
end
end
IO.puts A1.A2.m
#⇒ 42
Also, I can produce modules with functions from within a function call:
defmodule B1 do
def b2! do
defmodule B2 do
# enum is for the sake of future example
Enum.each %{m1: 42}, fn {_k, v} ->
# def b2(), do: unquote(v) WON’T WORK (WHY?), BUT
#v v
def b2(), do: #v
end
end
end
end
B1.b2! # produce a nested module
IO.puts B1.B2.b2 # call a method
#⇒ 42
Now my question is: how can I dynamically produce a module with dynamically created function names, e. g.:
defmodule B1 do
def b2! do
defmodule B2 do
Enum.each %{m1: 42, m2: 3.14}, fn {k, v} ->
#k k
#v v
def unquote(#k)(), do: #v # THIS DOESN’T WORK
end
end
end
end
NB I was able to achieve what I wanted with
defmodule B1 do
def b2! do
defmodule B2 do
Enum.each %{m1: 42, m2: 3.14}, fn {k, v} ->
ast = quote do: def unquote(k)(), do: unquote(v)
Code.eval_quoted(ast, [k: k, v: v], __ENV__)
end
end
end
end
but it seems to be quite hacky.
I believe this happens due to nested macro invocations (def and defmodule are both macros). If you place an unquote there, it unquotes from the top level def:
defmodule B1 do
k = :foo
v = :bar
def b2! do
defmodule B2 do
def unquote(k)(), do: unquote(v)
end
end
end
B1.b2!
IO.inspect B1.B2.foo
prints
:bar
The Module.create/3 recommends using that function to dynamically create modules when the body is an AST. With that, the code becomes much more elegant than the hacky solution using Code.eval_quoted/3:
defmodule B1 do
def b2! do
ast = for {k, v} <- %{m1: 42, m2: 3.14} do
quote do
def unquote(k)(), do: unquote(v)
end
end
Module.create(B1.B2, ast, Macro.Env.location(__ENV__))
end
end
B1.b2!
IO.inspect B1.B2.m1
IO.inspect B1.B2.m2
Output:
42
3.14

Scala case class, conditional Copy

I've defined a case class and a value:
scala> case class N(a:Int, b:Int)
defined class N
scala> val nnn = N(2,3)
nnn: N = N(2,3)
I would like to modify a field based on an optional value, t1 and t2 with type Option[Int], this is what i did:
val nnn1 = t1.map( x => nnn.copy( a = x)).getOrElse(nnn)
val nnn2 = t2.map( x => nnn1.copy( b = x)).getOrElse(nnn1)
Theres a Lens/Monocle/Scalaz way to do it generic?
A good approach colud be
def someF(i:Int) = // a great computation with i :)
val na = t1.map(someF).getOrElse(nnn.a)
val newnnn = nnn.copy(a = na, b = t2.getOrElse(nnn.b))
In a case where you have to apply someF to t1 is probably a good idea separate it.
No lens for you.

Best way to read lines in groups in a flat file

I have a file which consists of the groups of lines. Each group represents a event. The end of the group is denoted by "END". I can think of using a for loop to loop through the lines, store the intermediate lines and emit the group when "END" is encounter.
But since I would like to do it in Scala. I am wondering if someone can suggest a more functional way to accomplish the same thing?
----------
A
B
C
END
----------
D
E
F
END
----------
Just define an iterator to return groups
def groupIterator(xs:Iterator[String]) =
new Iterator[List[String]]
{ def hasNext = xs.hasNext; def next = xs.takeWhile(_ != "END").toList}
Testing (with an Iterator[String], but Source.getLines will return you an Iterator for the lines of your file)
val str = """
A
B
C
END
D
E
F
END
""".trim
for (g <- groupIterator(str.split('\n').toIterator)) println(g)
//> List(A, B, C)
//| List(D, E, F)

Scala, Parser Combinator for Tree Structured Data

How can parsers be used to parse records that spans multiple lines? I need to parse tree data (and eventually transform it to a tree data structure). I'm getting a difficult-to-trace parse error in the code below, but its not clear if this is even the best approach with Scala parsers. The question is really more about the problem solving approach rather than debugging existing code.
The EBNF-ish grammer is:
SP = " "
CRLF = "\r\n"
level = "0" | "1" | "2" | "3"
varName = {alphanum}
varValue = {alphnum}
recordBegin = "0", varName
recordItem = level, varName, [varValue]
record = recordBegin, {recordItem}
file = {record}
An attempt to implement and test the grammer:
import util.parsing.combinator._
val input = """0 fruit
1 id 2
1 name apple
2 type red
3 size large
3 origin Texas, US
2 date 2 aug 2011
0 fruit
1 id 3
1 name apple
2 type green
3 size small
3 origin Florida, US
2 date 3 Aug 2011"""
object TreeParser extends JavaTokenParsers {
override val skipWhitespace = false
def CRLF = "\r\n" | "\n"
def BOF = "\\A".r
def EOF = "\\Z".r
def TXT = "[^\r\n]*".r
def TXTNOSP = "[^ \r\n]*".r
def SP = "\\s".r
def level: Parser[Int] = "[0-3]{1}".r ^^ {v => v.toInt}
def varName: Parser[String] = SP ~> TXTNOSP
def varValue: Parser[String] = SP ~> TXT
def recordBegin: Parser[Any] = "0" ~ SP ~ varName ~ CRLF
def recordItem: Parser[(Int,String,String)] = level ~ varValue ~ opt(varValue) <~ CRLF ^^
{case l ~ f ~ v => (l,f,v.map(_+"").getOrElse(""))}
def record: Parser[List[(Int,String,String)]] = recordBegin ~> rep(recordItem)
def file: Parser[List[List[(Int,String,String)]]] = rep(record) <~ EOF
def parse(input: String) = parseAll(file, input)
}
val result = TreeParser.parse(input).get
result.foreach(println)
As Daniel said, you should better let the parser handle whitespace skipping to minimize your code. However you may want to tweak the whitespace value so you can match end of lines explicitly. I did it below to prevent the parser from moving to the next line if no value for a record is defined.
As much as possible, try to use the parsers defined in JavaTokenParsers like ident if you want to match alphabetic words.
To ease your error tracing, perform a NoSuccess match on parseAll so you can see at what point the parser failed.
import util.parsing.combinator._
val input = """0 fruit
1 id 2
1 name apple
2 type red
3 size large
3 origin Texas, US
2 var_without_value
2 date 2 aug 2011
0 fruit
1 id 3
1 name apple
2 type green
3 size small
3 origin Florida, US
2 date 3 Aug 2011"""
object TreeParser extends JavaTokenParsers {
override val whiteSpace = """[ \t]+""".r
val level = """[1-3]{1}""".r
val value = """[a-zA-Z0-9_, ]*""".r
val eol = """[\r?\n]+""".r
def recordBegin = "0" ~ ident <~ eol
def recordItem = level ~ ident ~ opt(value) <~ opt(eol) ^^ {
case l ~ n ~ v => (l.toInt, n, v.getOrElse(""))
}
def record = recordBegin ~> rep1(recordItem)
def file = rep1(record)
def parse(input: String) = parseAll(file, input) match {
case Success(result, _) => result
case NoSuccess(msg, _) => throw new RuntimeException("Parsing Failed:" + msg)
}
}
val result = TreeParser.parse(input)
result.foreach(println)
Handling whitespace explicitly is not a particularly good idea. And, of course, using get means you lose the error message. In this particular example:
[1.3] failure: string matching regex `\s' expected but `f' found
0 fruit
^
Which is actually pretty clear, though the question is why it expected a space. Now, this was obviously processing a recordBegin rule, which is defined thusly:
"0" ~ SP ~ varName ~ CRLF
So, it parsers the zero, then the space, and then fruit must be parsed against varName. Now, varName is defined like this:
SP ~> TXTNOSP
Another space! So, fruit should have began with a space.