In Rebol how to get the Parent in an Object Path? - rebol

Is there a function to get the parent of an object for example
parent-of system/console/history
would give
system/console

It seems that you don't realise that a path! value is a type of series! value:
>> path: 'system/console/history
== system/console/history
>> type? path
== path!
>> series? path
== true
So just remove the last value in the series:
>> path: head remove back tail path
== system/console

Peter is right if the history object has just one parent. But it may have others:
my-block: copy []
append my-block system/console/history
my-object: make object! [history: system/console/history]
history is now has three legitimate parents:
system/console
my-block
my-object/history
Which you consider to the the real parent is really up to you. There is no easy way that I know of to find all the contexts an object (or block) is part of.

Related

How to chain filter expressions together

I have data in the following format
ArrayList<Map.Entry<String,ByteString>>
[
{"a":[a-bytestring]},
{"b":[b-bytestring]},
{"a:model":[amodel-bytestring]},
{"b:model":[bmodel-bytestring]},
]
I am looking for a clean way to transform this data into the format (List<Map.Entry<ByteString,ByteString>>) where the key is the value of a and value is the value of a:model.
Desired output
List<Map.Entry<ByteString,ByteString>>
[
{[a-bytestring]:[amodel-bytestring]},
{[b-bytestring]:[bmodel-bytestring]}
]
I assume this will involve the use of filters or other map operations but am not familiar enough with Kotlin yet to know this
It's not possible to give an exact, tested answer without access to the ByteString class — but I don't think that's needed for an outline, as we don't need to manipulate byte strings, just pass them around. So here I'm going to substitute Int; it should be clear and avoid any dependencies, but still work in the same way.
I'm also going to use a more obvious input structure, which is simply a map:
val input = mapOf("a" to 1,
"b" to 2,
"a:model" to 11,
"b:model" to 12)
As I understand it, what we want is to link each key without :model with the corresponding one with :model, and return a map of their corresponding values.
That can be done like this:
val output = input.filterKeys{ !it.endsWith(":model") }
.map{ it.value to input["${it.key}:model"] }.toMap()
println(output) // Prints {1=11, 2=12}
The first line filters out all the entries whose keys end with :model, leaving only those without. Then the second creates a map from their values to the input values for the corresponding :model keys. (Unfortunately, there's no good general way to create one map directly from another; here map() creates a list of pairs, and then toMap() creates a map from that.)
I think if you replace Int with ByteString (or indeed any other type!), it should do what you ask.
The only thing to be aware of is that the output is a Map<Int, Int?> — i.e. the values are nullable. That's because there's no guarantee that each input key has a corresponding :model key; if it doesn't, the result will have a null value. If you want to omit those, you could call filterValues{ it != null } on the result.
However, if there's an ‘orphan’ :model key in the input, it will be ignored.

How to do without delete_if for Active Record?

When I do:
apartments = Apartment.where(apart_params).delete_if{|x| x.floor == x.max_floor }
Ruby's array method delete_if on Active record Relation object results in
NoMethodError (undefined method `delete_if' for #<Apartment::ActiveRecord_Relation:0x00000006d84ea0>
Did you mean? delete
delete_all):
I don't understand why it happens - this object seems to respond like a plain array...
Is there any smarter alternative except using plain each do block?
Maybe you can give an advise on making smart SQL (postgres) query to reject those apartments which are on the last floor?
The relation can be converted to an array and then delete_if can be called:
Apartment.where(apart_params).to_a.delete_if {|x| x.floor == x.max_floor }
About a smarter way, it depends if the floor and max_floor methods are columns. If so, then:
Apartment.where(apart_params).where.not("floor = max_floor")
I would suggest switching your apartments assignment to the following:
apartments = Apartment.where(apart_params).map{|x| x.floor == x.max_floor ? x.delete : x }
Even if the Apartment.where(apart_params) looks like a Array object, it isn't! As the error says, it returns a ActiveRecordRelation, that is not a Array. So, if you want to use array methods on a ActiveRecordRelation, you have to convert your object to a array with to_a
apartments = Apartment.where(apart_params).to_a.delete_if{|x| x.floor == x.max_floor }

How to require 1 or more of an argument in MAIN

Right now, I have a MAIN sub that can take one or more string arguments. But I am using two separate parameters for MAIN to do it:
sub MAIN (
Str:D $file,
*#files,
) {
#files.prepend: $file;
# Rest of the program
}
Now I am wondering if there's a more idiomatic way to achieve this, as my current solution feels a little clunky, and not very Perly.
You could do it with a proto sub
proto sub MAIN ( $, *# ){*}
multi sub MAIN ( *#files ) {
# Rest of the program
}
or with sub-signature deparsing
sub MAIN ( *#files ($,*#) ) {
# Rest of the program
}
At the risk of "over answering" - my take on "Perly" is concise as possible without becoming obscure (perhaps I'm just replacing one subjective term with two others... :-)
If you have a "slurpy" array as the only parameter, then it will happily accept no arguments which is outside the spec you put in the comments. However, a positional parameter is compulsory by default and proto's are only necessary if you want to factor out constraints on all multi's - presumably overkill for what you want here. So, this is enough:
sub MAIN($file , *#others) {
say "Received file, $file, and #others.elems() others."
}
This is close to what mr_ron put - but why not go with the default Usage message that MAIN kindly whips up for you by examining your parameters:
$ ./f.pl
Usage:
./f.pl <file> [<others> ...]
Some might say I cheated by dropping the Str type constraint on the first parameter but it really doesn't buy you much when you're restricting to strings because numerics specified at the CLI come through as type IntStr (a kind-of hybrid type) that satisfies a Str constraint. OTOH, when constraining CLI parameters to Num or Int, Perl6 will check that you're actually putting digits there - or at least, what unicode considers digits.
If you're wanting actual filenames, you can save yourself a validation step by constraining to type IO(). Then it will only work if you name a file. And finally, putting where .r after the parameter will insist that it be readable to boot:
sub MAIN(IO() $file where .r, *#others) { ...
One short line that insists on one compulsory argument that is a filename referencing a readable file, with a varying number of other parameters and a useful Usage message auto generated if it all goes sideways...
Perhaps good enough answer here:
sub MAIN(*#a where {.elems > 0 and .all ~~ Str}) {
say "got at least 1 file name"
}
sub USAGE {
say "{$*PROGRAM-NAME}: <file-name> [ <file-name> ... ]"
}
Based on docs here:
https://docs.perl6.org/type/Signature#Constraining_Slurpy_Arguments
You can also try and use simply dynamic variables:
die "Usage: $*EXECUTABLE <file> <file2>*" if !+#*ARGS;
my #files = #*ARGS;
where #*ARGS is an array with the arguments issued into the command line
You can even use #*ARGFILES, since they are actually files

check if 2 linked list have the same elements regardless of order

Is there any way to check if 2 linked lists have the same elements regardless of order.
edit question:
I have fixed the code and given some more details:
this is the method that compares 2 lists
compare: object2
^ ((mylist asBag) = ((objetc2 getList) asBag)).
the method belongs to the class myClass that has a field : myLList. myList is a linkedList of type element.
I have compiled it in the workspace:
a: = element new id:1.
b:= element new id:2.
c:=element new id:3.
d: = element new id:1.
e:= element new id:2.
f:=element new id:3.
elements1 := myClass new.
elements addFirst:a.
elements addFirst:b.
elements addFirst:c.
elements2 := myClass new.
elements addFirst:d.
elements addFirst:e.
elements addFirst:f.
Transcript show: (elements1 compare:elements2).
so I am getting false.. seems like it checks for equality by reference rather than equality by value..
So I think the correct question to ask would be: how can I compare 2 Bags by value? I have tried the '=='..but it also returned false.
EDIT:
The question changed too much - I think it deserves a new question for itself.
The whole problem here is that (element new id: 1) = (element new id: 1) is giving you false. Unless it's particular class (or superclasses) redefine it, the = message is resolved comparing by identity (==) by default. That's why your code only works with a collection being compared with itself.
Test it with, for example, lists of numbers (which have the = method redefined to reflect what humans understand by numeric equality), and it will work.
You should redefine your element's class' = (and hashCode) methods for this to work.
Smalltalk handles everything by reference: all there exist is an object, which know (reference) other objects.
It would be wrong to say that two lists are equivalent if they are in different order, as the order is part of what a list means. A list without an order is what we call a bag.
The asBag message (as all of the other as<anotherCollectionType> messages) return a new collection of the named type with all the elements of the receiver. So, #(1 2 3 2) is an Array of four elements, and #(1 2 3 2) asBag is a bag containing those four elements. As it's a Bag, it doesn't have any particular order.
When you do bagA := Bag new. you are creating a new Bag instance, and reference it with bagA variable. But then you do bagA := myList asBag, so you lose the reference to the previous bag - the first assignment doesn't do anything useful in your code, as you don't use that bag.
Saying aBool ifTrue: [^true] ifFalse: [^false] has exactly the same meaning as saying ^aBool - so we prefer just to say that. And, as you only create those two new bags to compare them, you could simplify your whole method like this:
compareTo: anotherList
^ myList asBag = anotherList asBag
Read it out loud: this object (whatever it is) compares to another list if it's list without considering order is the same than the other list without order.
The name compareTo: is kind of weird for returning a boolean (containsSameElements: would be more descriptive), but you get the point much faster with this code.
Just to be precise about your questions:
1) It doesn't work because you're comparing bag1 and bag2, but just defined bagA and bagB.
2) It's not efficient to create those two extra bags just because, and to send the senseless ifTrue: message, but other way it's OK. You may implement a better way to compare the lists, but it's way better to rely on the implementation of asBag and the Bag's = message being performant.
3) I think you could see the asBag source code, but, yes, you can assume it to be something like:
Collection>>asBag
|instance|
instance := Bag new.
instance addAll: self.
^instance
And, of course, the addAll: method could be:
Collection>>addAll: anotherCollection
anotherCollection do: [ :element | self add: element ]
So, yes - it creates a new Bag with all the receiver's elements.
mgarciaisaia's answer was good... maybe too good! This may sound harsh, but I want you to succeed if you're serious about learning, so I reiterate my suggestion from another question that you pick up a good Smalltalk fundamentals textbook immediately. Depending on indulgent do-gooders to rework your nonsensical snippets into workable code is a very inefficient way to learn to program ;)
EDIT: The question has changed dramatically. The following spoke to the original three-part question, so I paraphrased the original questions inline.
Q: What is the problem? A: The problem is lack of fundamental Smalltalk understanding.
Q: Is converting to bags an efficient way to make the comparison? A: Although it's probably not efficient, don't worry about that now. In general, and especially at the beginning when you don't have a good intuition about it, avoid premature optimization - "make it work", and then only "make it fast" if justified by real-world profiling.
Q: How does #asBag work? A: The implementation of #asBag is available in the same living world as your own code. The best way to learn is to view the implementation directly (perhaps by "browsing implementors" if you aren't sure where it's defined") and answer your own question!! If you can't understand that implementation, see #1.

How can I get the name of a node?

Is there a way to get the name of a node? For example:
Fruits:
- apple
- orange
- pear
and in C++:
YAML::Node fruit = parser["Fruits"];
cout << fruit.name() << endl; // should print "Fruits"
is there something like YAML::Node::name()? I don't see anything in Node.h that fits the bill.
If there isn't, any suggestions on a simple way to modify the code to record this information?
Thanks!
What you're really looking for is the key associated with a value in a map. You're right, there's no link back from a value to its key, but you can store one, when you're navigating the node in the first place.
If all of your keys are string keys, then whenever you pass a value to some function, just pass along the string key as well:
// Instead of:
doSomething(node[key]);
// Call:
doSomething(key, node[key]);
Fruits:
- apple
- orange
- pear
This is one key-value-pair
"Fruits" is the key
" -apple
-orange
-pear " is the value
YAML::Node fruit = parser["Fruits"];
This command use one key to query the value
So the node "fruit" just keep information about value.