why is this leafref invalid according to pyang - ietf-netmod-yang

For the following
module yy {
yang-version 1.1;
namespace "urn:example:yy";
prefix yyX;
container x4 {
leaf x5 {
type string;
}
}
grouping gx {
leaf l {
type leafref {
path "../x4/x5";
}
}
}
}
And
module tt {
yang-version 1.1;
namespace "urn:example:tt";
prefix ttX;
import yy {
prefix zz;
}
container ty {
uses zz:gx;
}
}
yang -f yin tt.yang
.\yy.yang:16: error: yy:x4 in the path for l at tt.yang:11 (at .\yy.yang:14) is not found
module yy does have container x4 , right ?
since leafrefs under grouping are resolved w.r.t the place where they are used, why does it not work if I move the container x4 to tt.yang instead ?

This is a nice example of why defining a non-reusable grouping is a bad idea. That is what your grouping represents. It "references" definitions in the context where it is defined (and not within the grouping itself), not where it is used.
The nodes in a grouping lack context (namespaces, etc.) until used. For example, you cannot tell which node is the parent node of leaf l, until the grouping is used - that is because groupings are not part of the schema tree until used. Upon usage, nodes in the grouping are copied to where the uses appears, thereby replacing it.
So what happens when you do use your grouping and the compiler hits your leafref? Well, it will try to find the referenced node in the context where the grouping is used, that is, the tt module. You used the grouping within the ty container, so the schema tree looks like this:
+--rw container ty
+--rw leaf l? leafref
Your expression is ../x4/x5, which in a longer form means parent::*/child::x4/child::x5. The initial context of the expression is the l leaf in the schema tree. So broken into three steps, your expression evaluates to:
+-----------+----------------+----------------+--------------+
| Step | Context | Result | Final result |
+-----------+----------------+----------------+--------------+
| parent::* | leaf l | container ty | |
| child::x4 | container ty | empty node set | |
| child::x5 | empty node set | empty node set | <-- |
+-----------+----------------+----------------+--------------+
Yep, there is noting that matches the expression.
If you move the container to the tt module, you get the following schema tree:
+--rw container x4
+--rw leaf x5? string
+--rw container tt
+--rw leaf l? leafref
You should be able to tell by now, that this isn't going to work either. The reason is - the grouping assumes the context in which it will be used, which is bad practice. Since the context does not match the expression, you get broken YANG.
Another important concept is the scope of the path expression. The expression is only able to "see" definitions reachable by the module where the grouping is used, not where it is defined. That is why the two example schema trees above are intentionally different.

Related

How do I cache hash codes for an AST?

I am working on a language in F# and upon testing, I find that the runtime spends over 90% of its time comparing for equality. Because of that the language is so slow as to be unusable. During instrumentation, the GetHashCode function shows fairly high up on the list as a source of overhead. What is going on is that during method calls, I am using method bodies (Expr) along with the call arguments as keys in a dictionary and that triggers repeated traversals over the AST segments.
To improve performance I'd like to add memoization nodes in the AST.
type Expr =
| Add of Expr * Expr
| Lit of int
| HashNode of int * Expr
In the above simplified example, what I would like is that the HashNode represent the hash of its Expr, so that the GetHashCode does not have to travel any deeper in the AST in order to calculate it.
That having said, I am not sure how I should override the GetHashCode method. Ideally, I'll like to reuse the inbuilt hash method and make it ignore only the HashNode somehow, but I am not sure how to do that.
More likely, I am going to have to make my own hash function, but unfortunately I know nothing about hash functions so I am a bit lost right now.
An alternative idea that I have would be to replace nodes with unique IDs while keeping that hash function as it is, but that would introduce additional complexities into the code that I'd rather avoid unless I have to.
I needed a similar thing recently in TheGamma (GitHub) where I build a dependency graph (kind of like AST) that gets recreated very often (when you change code in editor and it gets re-parsed), but I have live previews that may take some time to calculate, so I wanted to reuse as much of the previous graph as possible.
The way I'm doing that is that I attach a "symbol" to each node. Two nodes with the same symbol are equal, which I think you could use for efficient equality testing:
type Expr =
| Add of ExprNode * ExprNode
| Lit of int
and ExprNode(expr:Expr, symbol:int) =
member x.Expression = expr
member x.Symbol = symbol
override x.GetHashCode() = symbol
override x.Equals(y) =
match y with
| :? ExprNode as y -> y.Symbol = x.Symbol
| _ -> false
I do keep a cache of nodes - the key is some code of the node kind (0 for Add, 1 for Lit, etc.) and symbols of all nested nodes. For literals, I also add the number itself, which will mean that creating the same literal twice will give you the same node. So creating a node looks like this:
let node expr ctx =
// Get the key from the kind of the expression
// and symbols of all nested node in this expression
let key =
match expr with
| Lit n -> [0; n]
| Add(e1, e2) -> [1; e1.Symbol; e2.Symbol]
// Return either a node from cache or create a new one
match ListDictionary.tryFind key ctx with
| Some res -> res
| None ->
let res = ExprNode(expr, nextId())
ListDictionary.set key res ctx
res
The ListDictionary module is a mutable dictionary where the key is a list of integers and nextId is the usual function to generate next ID:
type ListDictionaryNode<'K, 'T> =
{ mutable Result : 'T option
Nested : Dictionary<'K, ListDictionaryNode<'K, 'T>> }
type ListDictionary<'K, 'V> = Dictionary<'K, ListDictionaryNode<'K, 'V>>
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module ListDictionary =
let tryFind ks dict =
let rec loop ks node =
match ks, node with
| [], { Result = Some r } -> Some r
| k::ks, { Nested = d } when d.ContainsKey k -> loop ks (d.[k])
| _ -> None
loop ks { Nested = dict; Result = None }
let set ks v dict =
let rec loop ks (dict:ListDictionary<_, _>) =
match ks with
| [] -> failwith "Empty key not supported"
| k::ks ->
if not (dict.ContainsKey k) then
dict.[k] <- { Nested = Dictionary<_, _>(); Result = None }
if List.isEmpty ks then dict.[k].Result <- Some v
else loop ks (dict.[k].Nested)
loop ks dict
let nextId =
let mutable id = 0
fun () -> id <- id + 1; id
So, I guess I'm saying that you'll need to implement your own caching mechanism, but this worked quite well for me and may hint at how to do this in your case!

Multiple queries (OR statements) using find

Currently to perform multiple queries using find, I invoke each query individually separated by |
index = find(strcmp(data{:,{'type'}},'A') | strcmp(data{:,{'type'}},'B') | strcmp(data{:,{'type'}},'C') | strcmp(data{:,{'type'}},'D'));
To find all rows that where the field 'type' contains either A, B, C or D.
data is held in a table hence the usage of }.
Is there a more concise way of doing this without the need to specify the query in full each time?
You could use ismember instead of multiple uses of strcmp.
index = find(ismember(data{:,{'type'}}, {'A','B','C','D'}));
An alternative (because ismember will probably be slower than multiple uses of strcmp) would be to factor out the repeated code -
x = data{:, {'type'}}; %# This isn't valid MATLAB but whatever...
index = find(strcmp(x,'A') | strcmp(x,'B') | strcmp(x,'C') | strcmp(x,'D'));
You could also use multiple lines for readability
x = data{:, {'type'}}; %# This isn't valid MATLAB but whatever...
index = find(strcmp(x,'A') ...
| strcmp(x,'B') ...
| strcmp(x,'C') ...
| strcmp(x,'D'));

Defining own instance classes

I struggle with creating own instances for my datatypes.
I defined a type like:
data Breakfast = Egg | Sausage Int | Bread Breakfast deriving (Eq, Show)
and want it to be an instance of the class Ord. I want to compare it by some rules like: A Egg is as good as 2 Sausages etc.
I tried it like this:
instance Ord a => Ord (Breakfast) where
compare (Egg) (Sausage 2) = EQ
...
but I get the error:
Variable a occurs more often than in the instance head.
i tried another example and this works fine:
data Down a = Down a deriving (Eq, Show, Read)
instance Ord a => Ord (Down a) where
compare (Down x) (Down y) = y `compare` x
Hope you guys can help me whats wrong. Im very new to Haskell. Thanks
Just remove the constraint on your instance definition:
instance Ord Breakfast where
compare (Egg) (Sausage 2) = EQ
...
You had a constraint on a type variable a which wasn't used on the right-hand side (the instance head).

Best way to create generic/method consistency for sort.data.frame?

I've finally decided to put the sort.data.frame method that's floating around the internet into an R package. It just gets requested too much to be left to an ad hoc method of distribution.
However, it's written with arguments that make it incompatible with the generic sort function:
sort(x,decreasing,...)
sort.data.frame(form,dat)
If I change sort.data.frame to take decreasing as an argument as in sort.data.frame(form,decreasing,dat) and discard decreasing, then it loses its simplicity because you'll always have to specify dat= and can't really use positional arguments. If I add it to the end as in sort.data.frame(form,dat,decreasing), then the order doesn't match with the generic function. If I hope that decreasing gets caught up in the dots `sort.data.frame(form,dat,...), then when using position-based matching I believe the generic function will assign the second position to decreasing and it will get discarded. What's the best way to harmonize these two functions?
The full function is:
# Sort a data frame
sort.data.frame <- function(form,dat){
# Author: Kevin Wright
# http://tolstoy.newcastle.edu.au/R/help/04/09/4300.html
# Some ideas from Andy Liaw
# http://tolstoy.newcastle.edu.au/R/help/04/07/1076.html
# Use + for ascending, - for decending.
# Sorting is left to right in the formula
# Useage is either of the following:
# sort.data.frame(~Block-Variety,Oats)
# sort.data.frame(Oats,~-Variety+Block)
# If dat is the formula, then switch form and dat
if(inherits(dat,"formula")){
f=dat
dat=form
form=f
}
if(form[[1]] != "~") {
stop("Formula must be one-sided.")
}
# Make the formula into character and remove spaces
formc <- as.character(form[2])
formc <- gsub(" ","",formc)
# If the first character is not + or -, add +
if(!is.element(substring(formc,1,1),c("+","-"))) {
formc <- paste("+",formc,sep="")
}
# Extract the variables from the formula
vars <- unlist(strsplit(formc, "[\\+\\-]"))
vars <- vars[vars!=""] # Remove spurious "" terms
# Build a list of arguments to pass to "order" function
calllist <- list()
pos=1 # Position of + or -
for(i in 1:length(vars)){
varsign <- substring(formc,pos,pos)
pos <- pos+1+nchar(vars[i])
if(is.factor(dat[,vars[i]])){
if(varsign=="-")
calllist[[i]] <- -rank(dat[,vars[i]])
else
calllist[[i]] <- rank(dat[,vars[i]])
}
else {
if(varsign=="-")
calllist[[i]] <- -dat[,vars[i]]
else
calllist[[i]] <- dat[,vars[i]]
}
}
dat[do.call("order",calllist),]
}
Example:
library(datasets)
sort.data.frame(~len+dose,ToothGrowth)
Use the arrange function in plyr. It allows you to individually pick which variables should be in ascending and descending order:
arrange(ToothGrowth, len, dose)
arrange(ToothGrowth, desc(len), dose)
arrange(ToothGrowth, len, desc(dose))
arrange(ToothGrowth, desc(len), desc(dose))
It also has an elegant implementation:
arrange <- function (df, ...) {
ord <- eval(substitute(order(...)), df, parent.frame())
unrowname(df[ord, ])
}
And desc is just an ordinary function:
desc <- function (x) -xtfrm(x)
Reading the help for xtfrm is highly recommended if you're writing this sort of function.
There are a few problems there. sort.data.frame needs to have the same arguments as the generic, so at a minimum it needs to be
sort.data.frame(x, decreasing = FALSE, ...) {
....
}
To have dispatch work, the first argument needs to be the object dispatched on. So I would start with:
sort.data.frame(x, decreasing = FALSE, formula = ~ ., ...) {
....
}
where x is your dat, formula is your form, and we provide a default for formula to include everything. (I haven't studied your code in detail to see exactly what form represents.)
Of course, you don't need to specify decreasing in the call, so:
sort(ToothGrowth, formula = ~ len + dose)
would be how to call the function using the above specifications.
Otherwise, if you don't want sort.data.frame to be an S3 generic, call it something else and then you are free to have whatever arguments you want.
I agree with #Gavin that x must come first. I'd put the decreasing parameter after the formula though - since it probably isn't used that much, and hardly ever as a positional argument.
The formula argument would be used much more and therefore should be the second argument. I also strongly agree with #Gavin that it should be called formula, and not form.
sort.data.frame(x, formula = ~ ., decreasing = FALSE, ...) {
...
}
You might want to extend the decreasing argument to allow a logical vector where each TRUE/FALSE value corresponds to one column in the formula:
d <- data.frame(A=1:10, B=10:1)
sort(d, ~ A+B, decreasing=c(A=TRUE, B=FALSE)) # sort by decreasing A, increasing B

SML/ML Int to String conversion

I have this code:
datatype 'a Tree = Empty | LEAF of 'a | NODE of ('a Tree) list;
val iL1a = LEAF 1;
val iL1b = LEAF 2;
val iL1c = LEAF 3;
val iL2a = NODE [iL1a, iL1b, iL1c];
val iL2b = NODE [iL1b, iL1c, iL1a];
val iL3 = NODE [iL2a, iL2b, iL1a, iL1b];
val iL4 = NODE [iL1c, iL1b, iL3];
val iL5 = NODE [iL4];
fun treeToString f Node = let
fun treeFun (Empty) = ["(:"]
| treeFun (NODE([])) = [")"]
| treeFun (LEAF(v)) = [f v]
| treeFun (NODE(h::t)) = [""] # ( treeFun (h)) # ( treeFun (NODE(t)) )
in
String.concat(treeFun Node)
end;
treeToString Int.toString iL5;
When I run my function I get the output: "32123)231)12)))".
The answer should be "((32((123)(231)12)))".
I've tried modifying my function to add ( in every place I can think but I cannot figure out where I should be adding "(". Where have I messed up?
Edit: I believe I need to use map or List.filter somewhere, but am not sure where.
It looks like your method of recursion over the tail of a list node is the problem. Instead of treeFun h appended to treefun (NODE(t)), try using this for the NODE case:
treeFun (NODE(items)) = ["("] # List.concat (map treeFun items) # [")"]
That is, map treeFun over the entire contents of the node, and surround the results with "(" and ")". That definition might be a bit too terse for you to understand what's going on, so here's a more verbose form that you might find clearer:
| treeFun (NODE(items)) =
let val subtree_strings : string list list = map treeFun items
val concatenated_subtrees : string list = List.concat subtree_strings
in ["("] # concatenated_subtrees # [")"]
end
subtree_strings is the result of taking all the subtrees in the given node, and turning each of them to a list of strings by recursively calling treeFun on each subtree. Since treeFun gives back a list of strings each time it's called, and we're calling it on an entire list of subtrees, the result is a corresponding list of lists of subtrees. So for instance, if we called map treeFun [LEAF 1, LEAF 2, LEAF 3], we'd get back [["1"], ["2"], ["3"]].
That's not the answer we want, since it's a list of lists of strings rather than a list of plain strings. We can fix that using List.concat, which takes a list of lists, and forms a single list of all the underlying items. So for instance List.concat [["1"], ["2"], ["3"]] returns ["1", "2", "3"]. Now all we have to do is put the parentheses around the result, and we're done.
Notice that this strategy works just as well for completely empty nodes as it does for nodes with one or more subtrees, so it eliminates the need for the second case of treeFun in your original definition. Generally, in ML, it's a code smell if a function of one argument doesn't have exactly one case for each constructor of the argument's type.