5 ms·
Blocks are objects themselves and they preserve their access to the local variable scope (i.e. they are "lexical closures"). This allows you to write control st
by wedesoft 17y ago
Blocks are objects themselves and they preserve their access to the local variable scope (i.e. they are "lexical closures"). This allows you to write control structures yourself. E.g. if-statement:
def my_if( cond )
if cond
yield
end
end
x = 3
my_if x < 5 do
puts "#{x} is lower than 5"
end
# prints '3 is lower than 5'
Unfortunately the syntax for a control structure accepting more than one block is less elegant. But you don't need to compromise on the semantics:
def my_if_else( cond, a, b )
if cond
a.call
else
b.call
end
end
x = 2
my_if_else( x < 0, proc do
puts "#{x} is lower than zero"
end, proc do
puts "#{x} is greater or equal zero"
end )
# prints '2 is greater or equal zero'
- kscaldef 17y agoSure and I understand all that. My comment was just that the examples in the article are perhaps not the best ones to use because they are simply replicating what other languages do with lexically scoped variables, and users of those languages view the lack of such as a deficiency of Ruby.
- swannodette 17y agoHow does a lexically scoped variable close the file? I couldn't find any documentation about this while Google searching. Is this a real language feature where you can specify what happens to a variable when it goes out of scope? In other words, is it a baked-in convenience or a real extensible abstraction?
- kscaldef 17y agoYes, you can absolutely specify a destructor which will be called when a lexical / local variable goes out of scope in Perl or C++ (just to name the most well-known languages with this feature).
- wedesoft 17y ago#include <fstream> using namespace std; int main() { { fstream f( "test.txt", ios::out ); f << "Hello" << endl; } // File gets closed // ... return 0; }
- weaksauce 17y agoI haven't done C++ in a while but is it possible to allocate an fstream object on the heap instead of the stack? If so and you do not explicitly call delete on it then will it still close the file when going out of scope? Something like: #include <fstream> using namespace std; int main() { { fstream *f = new fstream( "test.txt", ios::out ); f << "Hello" << endl; } // memory leak and non closed file. // ... return 0; }
- scott_s 17y agoYou can allocate an fstream (or any) object on the heap, and if you do not explicitly call delete it will not close the file. Resource-acquisition-is-object-instantiation (RAIOI) is a common C++ idiom, and it only works on stack variables.
- altano 17y agoIt only works with stack variables, but you can wrap the construction and destruction of anything, including heap-allocated objects, with a stack variable. You tie the allocation of the heap object to the constructor of the stack variable, and the reverse for de-allocation/destructor. The premise of all this is that the construction and destruction of stack variables both happen at well known times and are guaranteed to occur.
- wedesoft 17y agoNo. But you can put a smart pointer object on the stack. #include <boost/smart_ptr.hpp> #include <fstream> using namespace boost; using namespace std; int main() { { shared_ptr< fstream > f( new fstream( "test.txt", ios::out ) ); *f << "Hello" << endl; } // File gets closed // ... return 0; }
- draegtun 17y agoRuby does have a nice syntax for passing a single block. But like you say it starts to become a little inelegant when more than one block is used. Strangely Perl copes with multi "blocks" far nicer IMHO: sub my_if_else { my ($cond, $then, $else) = @_; if ( $cond ) { $then->() } else { $else->() } } my $x = 2; my_if_else $x < 0, sub { say "$x is lower than zero" }, sub { say "$x is greater or equal to zero" }; And it can be even more window dressed by using the fat comma: my_if_else $x < 0 => sub { say "$x is lower than zero" } => sub { say "$x is greater or equal to zero" };
- wedesoft 17y agoWell, you could do something like this in Ruby: def my_if_else( cond, a, b ) if cond a.call else b.call end end x = 2 my_if_else x < 0, proc { "#{x} is lower than zero" }, proc { "#{x} is greater or equal zero" } My point is that you can't do without 'proc' (or 'sub' in your case).
- draegtun 17y agoOpps, my last comment/code got cut off :( Here goes again.... And going into sub prototype sublime you can also do: sub then (&@) { @_ } sub elsedo (&@) { @_ } my $x = 2; my_if_else $x < 0, then { say "$x is lower than zero" } elsedo { say "$x is greater or equal to zero" }; But yes, I do get your point about proc/sub. There is only so far you could (and should!) stretch the parsers syntax. There is always macros (see Devel::Declare in Perl) if you're mad enough to want cosmetic purity :)
- extension 17y agoFor the multi-block conditional, there is this idiom: def assuming cond Thread.current[:last_cond] = cond Thread.current[:last_value] = (yield if cond) end def otherwise if Thread.current[:last_cond] Thread.current[:last_value] else yield end end def alternately_if cond, &b otherwise { assuming cond, &b } end def how_big x assuming x < 3 do "small" end alternately_if x < 7 do "medium" end otherwise do "large" end end 10.times {|z| puts how_big z } Seems a bit sketchy though. It could be made cleaner by making them methods of an object that holds the state, but I guess that is starting to defeat the purpose.