Forum: Ruby Missing Something

Posted by Ron S. (zzzbutton)
on 2012-11-19 16:37
I am an amateur programmer, and I feel I am missing something. Whenever
I've tried to understand certain aspects of programming, I often feel I
am just "missing something." And most instruction doesn't help (usually
because of overly clever examples... like: Okay class, today we are
talking about classes. So we'll use this class as an example. We'll
create a "class" called "class" and since classes have members we'll
create a member for each member of the class. Then we'll create a
variable in the class called "class" where we'll store how classy
everyone in the class is. Now... how many of you wear glasses?)

I really just want my programs to DO SOMETHING. And I am trying to
develop a sort of automated analog synthesizer that works with files in
the .wav format. So my question is... what is WRONG with something like
this?

#! usr/bin/env ruby
require 'wavefile'
include Math

frames = (ARGV[0].to_f * 44100.0).to_i
volume = ARGV[1].to_f
freq   = 44100.0 / (ARGV[2].to_f)
file   = ARGV[3]
w      = WaveFile.new(1, 44100, 16)

(0..frames).each do
  |frame|
  w.sample_data[frame] = sin(frame * 2.0 * PI / freq) * volume * 32767
end

w.save(file)
Posted by Chris Hulan (Guest)
on 2012-11-19 17:05
(Received via mailing list)
The code looks good, as a one-off quick-n-dirty get it done script
If this is going to be used by others, and possibly re-used as part of a
larger app
than you have to think of how easy is it to integrate, and how easy is 
it
to change?

Some things to consider:
How is a user to know/learn what options are required/valid when running
the command?

What if you decide to add a GUI?

What if you wanted to process more than 1 file?
Posted by Ron S. (zzzbutton)
on 2012-11-19 17:27
Thanks, Chris! I have spent time thinking about ALL of those things. 
Your questions hit at the heart of the problem, and they hit me all at 
once, all the time, and I don't even know where to begin. This is a very 
simple version of what I am doing. I have much more functionality in 
other versions.

You hit the nail on the head with the first question. I never know when 
to export details to functions. I never know when to bundle functions 
into classes. I can't even begin to imagine how I would do things like 
modules or who knows what.

I have a help file that keys off of --help -h -? as an argument. That is 
usually in there before the requires and includes.

if (ARGV[0..3] == "--help")
  print "bleah... bleah... bleah..."
  exit 0
end

That's for if I want the end product to be CLI and scripty. It would 
work off of a file of some sort (.yaml) to feed in data.

The GUI option is fun, but unnecessary for what I want to do now. I 
suppose I would try to link it all up with Shoes?

The "processing" more than one file question hits the heart of the 
matter. First of all, this creates a file. The processing of files is a 
whole separate thing (in my way of viewing this). I want the final thing 
to be working on dozens, if not hundreds and thousands of objects. And 
that's where the "object orientation," "classes," "methods" and all that 
enters in and it's where I get lost. It's all so arbitrary. I can define 
anything to be anything. And the more I do, the more ideas I get until I 
end up with a great big jumble that falls off the rails.

All of the stuff out there (the help stuff) is operating on 
presumptions... unstated presumptions about what a User is trying to do. 
I always get to this point and get lost, hopelessly lost. Thanks for 
responding.
Posted by unknown (Guest)
on 2012-11-19 17:41
(Received via mailing list)
Am 19.11.2012 17:05, schrieb Chris Hulan:
>
> What if you decide to add a GUI?
>
> What if you wanted to process more than 1 file?

and:

What if you wanted to add functionality?

What if you wanted to parse and validate command line options?

What if you wanted to add tests for parts of your code?


But: for really simple tasks I would have no problem with
a script like yours.
Posted by "Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> (Guest)
on 2012-11-19 17:49
(Received via mailing list)
On Mon, Nov 19, 2012 at 5:27 PM, Ron S. <lists@ruby-forum.com> wrote:
> The "processing" more than one file question hits the heart of the
> matter. First of all, this creates a file. The processing of files is a
> whole separate thing (in my way of viewing this). I want the final thing
> to be working on dozens, if not hundreds and thousands of objects. And
> that's where the "object orientation," "classes," "methods" and all that
> enters in and it's where I get lost. It's all so arbitrary. I can define
> anything to be anything. And the more I do, the more ideas I get until I
> end up with a great big jumble that falls off the rails.

I think you should go step by step, little by little. Adding
functionality and looking for opportunities to reuse code. For
example, if you want to generate a file, and the process it, you can
start by defining a class that represents the file, so that you can
create such object and pass it around. Then you start thinking what
things can be responsibility of that class, things that you would tell
it to do, for example: save yourself in the filesystem. Create a
method for that. If you start this way, you will find yourself with
little reusable things that you can compose to perform more and more
complex logic.

For example:

class MyWaveFile
  def initialize frames, frequency, volume, file_name
    @frames, @frequency, @volume, @file_name = frames, frequency,
volume, file_name
    @w = WaveFile.new(1, 44100, 16) # don't know what these numbers mean
    generate_samples
  end

  def generate_samples
    (0..@frames).each do |frame|
      @w.sample_data[frame] = sin(frame * 2.0 * PI / @freq) * @volume * 
32767
  end

  def save
    @w.save @file_name
  end
end

Now you can:

wave_file = MyWaveFile.new frames, frequency, volume, file
wave_file.save

Later, for processing, you might have several different processing
algorithms (I have absolutely no clue about sound processing). You can
create a class for each processing:

class XProcessing
end
clas YProcessing
end
...

and apply the processings in order or whatever, passing your wave_file 
around:

wave_file = MyWaveFile.new frames, frequency, volume, file
XProcessing.new.process wave_file
wave_file.save

If, for example, you see duplicated code in all processing classes,
try to extract it to a common class (for example, a parent class of
all processings). This way, reducing duplication, you end up with a
design for your system.

It's difficult at the beggining, and depending on the domains can be
quite complex, but keep working at it and you'll see that it gets
easier and easier.

Good luck !

Jesus.
Posted by Ron S. (zzzbutton)
on 2012-11-19 19:26
Jesus -

You know exactly what I am getting at. Thank you for the reply. I will 
keep slogging away and hoping it gets easier. I am glad to have 
confirmation for what I suspected: That it is possible for a program to 
organically develop out of a linear sort of stack-oriented approach. I 
still don't see the advantage to bundling methods with objects. I guess 
I still think Commodore 64 style! I don't see why I shouldn't keep 
methods and objects separate is what I am saying. Methods as functions 
and objects as collections of arrays.

I am always doing things the hard way, knowing there is an easier way, 
but not quite knowing what the easier way is.

I'll answer a few of your questions, if you're curious...

> @w = WaveFile.new(1, 44100, 16) # don't know what these numbers mean

1 - means "mono" or one channel.
44100 - is 44,100 frames per second (that's the rate of a Compact Disc)
16 - is for 16 bit.

A .wav file is like a bitmap, it's uncompressed. So they're HUGE. 
Basically, the file tells a speaker where to be 44,100 times a second to 
an accuracy of 16 bits. The resting position of the speaker is 0. Fully 
out is 32768 and fully in is 32767. A speaker is basically an air pump.

That's it. Sound is really simple.

> Later, for processing, you might have several different processing
> algorithms (I have absolutely no clue about sound processing). You can
> create a class for each processing:

That's where things are slightly difficult. It is hard to distinguish 
between what is processing, what is filtering and what is generating. 
It's all arbitrary, actually. The great thing about doing this in 
software is that it's all just moving numbers around. No wires, no 
circuit boards, no keys, nothing. It's very pure in a way. So, one is 
always torn between what makes sense in the abstract, and what is 
familiar to the end user.

Thank you for your attention and code samples, Jesus. I think this will 
really help me organize my thinking.

- Ron
Posted by unknown (Guest)
on 2012-11-19 20:03
(Received via mailing list)
Am 19.11.2012 19:26, schrieb Ron S.:
> Jesus -
>
> You know exactly what I am getting at. Thank you for the reply. I will
> keep slogging away and hoping it gets easier. I am glad to have
> confirmation for what I suspected: That it is possible for a program to
> organically develop out of a linear sort of stack-oriented approach. I

Growing and restructuring / refactoring is the normal way.

> still don't see the advantage to bundling methods with objects. I guess
> I still think Commodore 64 style! I don't see why I shouldn't keep
> methods and objects separate is what I am saying. Methods as functions
> and objects as collections of arrays.

One advantage: try working with functions that depend on half a dozen
of parameters (and need to be called with half a dozen of arguments)
and compare with a method that can simply use the instance variables.

[...]

Tip: for a nicer and more robust way to handle command line options
have a look at the OptionParser class.
Posted by "Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> (Guest)
on 2012-11-19 20:09
(Received via mailing list)
On Mon, Nov 19, 2012 at 7:26 PM, Ron S. <lists@ruby-forum.com> wrote:
> Jesus -
>
> You know exactly what I am getting at. Thank you for the reply. I will
> keep slogging away and hoping it gets easier. I am glad to have
> confirmation for what I suspected: That it is possible for a program to
> organically develop out of a linear sort of stack-oriented approach. I
> still don't see the advantage to bundling methods with objects.

Some interesting concepts about object orientation are among other
encapsulation and reusability.
If you build an object with a clean interface, the clients of the
object can forget about the internal implementation.
The advantages of this is that everything from the outside is easier,
and you are free to change the implementation without the external
code noticing (or failing).

When you have these objects with a defined behaviour and a clean
interface, they can be reused in scenarios that not even you
anticipated when you created them, and this is huge.

You should also read about the SOLID design principles and clean code,
which can be summarized with: a good design is one that is easy to
change.

Tomorrow, when you have to change some logic in your code or add some
behaviour, you would prefer that everything is in a single place, in
the *expected* place. You use a class, extend it or whatever in a
simple way and "magically" you have changed your software. All or most
of the SOLID principles involve removing duplication: when you have
the same code in two different places, the risks are huge. If you need
to change that piece of logic, you might forget one of the two (or
many) places, leading to bugs. Objects help with this thanks to the
encapsulation and reusability they provide.

> I guess
> I still think Commodore 64 style! I don't see why I shouldn't keep
> methods and objects separate is what I am saying. Methods as functions
> and objects as collections of arrays.

If the functions operate over a set of data, it's better to have them
bundled with the data as a unit, it's easier to reason about it,
change implementation, extract common behaviour with other parts of
the code, etc, etc.

> I am always doing things the hard way, knowing there is an easier way,
> but not quite knowing what the easier way is.

Well, don't despair. These things take experience. It's important to
read about this topics, and to see other's code. A book that I liked a
lot was Clean Code from Robert C. Martin.

> an accuracy of 16 bits. The resting position of the speaker is 0. Fully
> out is 32768 and fully in is 32767. A speaker is basically an air pump.
>
> That's it. Sound is really simple.

Yep, sounds simple (pun intended)

> familiar to the end user.
The idea is that you create object that represents the concepts that
are in your domain: a filter chain composed of filters (two classes),
a generating algorithm (a factory object), transformations, etc.

> Thank you for your attention and code samples, Jesus. I think this will
> really help me organize my thinking.

Keep at it, and read about object oriented concepts, there are good
articles and books out there.

Jesus.
Posted by Ron S. (zzzbutton)
on 2012-11-21 09:27
Thank you so much, Jesus! You given me more practical advice AND theory 
in a few posts than I've gotten from dozens of sites! And now the sites 
are starting to make sense to boot. You've been a great help. Many 
blessings on your house!
Posted by Ron S. (zzzbutton)
on 2012-11-21 13:23
unknown wrote in post #1085232:

> One advantage: try working with functions that depend on half a dozen
> of parameters (and need to be called with half a dozen of arguments)
> and compare with a method that can simply use the instance variables.

I am already there! It's amazing how quickly the parameters list grows. 
Then I realize that I could use fewer parameters, but have to repeat 
code-snippets (let's say you have parameters: x, y, z... but z is (10 * 
x / y). You could pass z or just pass x and y but then you would have to 
pepper the method with 10 * x / y all over the place.) What I do is pass 
as few parameters as possible, and have local variables defined in the 
method.

> Tip: for a nicer and more robust way to handle command line options
> have a look at the OptionParser class.

That is a built-in?

Thank you for replying Unknown!
Posted by unknown (Guest)
on 2012-11-21 22:21
(Received via mailing list)
Am 21.11.2012 13:23, schrieb Ron S.:
>> Tip: for a nicer and more robust way to handle command line options
>> have a look at the OptionParser class.
>
> That is a built-in?

Yes, it is, see

http://ruby-doc.org/stdlib-1.9.3/libdoc/optparse/r...

You won't have to install a gem.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.