SHACL - Attribute is optional, but if it exists, it has to have once a specific value - validation

a class has an optional attribute. If you use this attribute, you have to set it once to a specific value. All other uses of this attribute are not restricted.
The real world example is dcat:themeTaxonomy used in dcat:Catalog in DCAT-AP. If used, it has to be set to <http://publications.europa.eu/resource/authority/data-theme> but the cardinality is *, so you are allowed to use other values.
Other use-cases would be a language skills. You have to state you overall skill-level, but when you do, you can add hearing, speaking or reading skills.
I am trying to check this constraint with SHACL.
The basic idea is:
I have a property shape with the path to the attribute. There I use sh:or. Either the sh:maxCount is 0 or the sh:minCount is 1. In the second case a sh:qualifiedValueShape would check the additional conditions:
Shapes
(omitted urls to not look like spam)
ex:valueIfExists
sh:message "Custom message for ex:valueIfExists!" ;
sh:path ex:optionalValue ;
sh:or ( [
sh:maxCount 0;
] [
sh:minCount 1;
sh:qualifiedValueShape [
sh:path ex:is ;
sh:hasValue "ok" ;
] ;
sh:qualifiedMinCount 1 ;
] ) ;
.
ex:mainShape
a sh:NodeShape ;
sh:targetClass ex:TestClass ;
sh:message "Custom message in mainShape!" ;
sh:property ex:valueIfExists;
.
Test data
(omitted urls to not look like spam)
ex:Test_OK_nothing
a ex:TestClass ;
.
ex:Test_OK_1
a ex:TestClass ;
ex:optionalValue [ex:is "ok"] ;
.
ex:Test_OK_1_2
a ex:TestClass ;
ex:optionalValue [ex:is "ok"] ;
ex:optionalValue [ex:is "other 1"] ;
ex:optionalValue [ex:is "other 2"] ;
.
ex:Test_Not_OK
a ex:TestClass ;
ex:optionalValue [ex:is "other 1"] ;
ex:optionalValue [ex:is "other 2"] ;
.
With this setup I get the following error:
VALIDATION FAILURE: Cannot find validator for constraint component http://www.w3.org/ns/shacl#MaxCountConstraintComponent
so SHACL doesn't want me to use min and maxCount at this position. Fine, I am not fixated on that, but I have no idea left on how to solve the problem.
Has anyone a solution?

Related

limit SHACL class to one of the specific types and nothing else

Suppose I have data like:
<http://domain/mySubj> abc:someProperty <http://domain/myObj>.
<http://domain/myObj> a <http://domain/Type1>.
<http://domain/myObj> a <http://domain/Type2>.
<http://domain/myObj> a <http://domain/WrongType>.
I want to write a SHACL rule that raise a violation if myObj has any type other that Type1 and Type2.
I have tried to write it like
sh:property [
a sh:PropertyShape ;
sh:path abc:someProperty ;
sh:or (
[ sh:class <http://domain/Type1> ; ]
[ sh:class <http://domain/Type2> ; ]
) ;
...
But this will not raise a violation since sh:or will pass as soon as one of the tests are correct. I can't also use sh:xone since this will need only one of Type1 and Type2 and not both, to be present. Even if I try to say sh:or([sh:xone (type1 type2)] [sh:and (type1 type2)]) my test will not produce the correct result since the and test will pass because we have type1 and type2 present and don't care about the Wrongtype.
Is there a way that I can say the class should be one or more things from a specific set of things and not anything else.
If this is about direct rdf:types only, I believe you can solve this using sh:in on the rdf:type of the values:
sh:property [
a sh:PropertyShape ;
sh:path abc:someProperty ;
sh:property [
sh:path rdf:type ;
sh:in ( abc:Type1 abc:Type2 ) ;
] ;
]

Stretching words and quotation scoping

To play at Stretch the word, I've defined the following words, to try to work at the problem via the same method as this answer:
USING: kernel math sequences sequences.repeating ;
IN: stretch-words
! "bonobo" -> { "b" "bo" "bon" "bono" "bonob" "bonobo" }
: ascend-string ( string -- ascending-seqs )
dup length 1 + iota [ 0 swap pick subseq ] map
[ "" = not ] filter nip ;
! expected: "bonobo" -> "bonoobbooo"
! actual: "bonobo" -> "bbbooonnnooobbbooo"
: stretch-word ( string -- stretched )
dup ascend-string swap zip
[
dup first swap last
[ = ] curry [ dup ] dip count
repeat
] map last ;
stretch-word is supposed to repeat a character in a string by the number of times it's appeared up to that position in the string. However, my implementation is repeating all instances of the 1string it gets.
I have the feeling this is easily implementable in Factor, but I can't quite figure it out. How do I make this do what I want?
Hm... not a great golf, but it works...
First, I made a minor change to ascend-string so it leaves the string on the stack:
: ascend-string ( string -- string ascending-seqs )
dup length 1 + iota [ 0 swap pick subseq ] map
[ "" = not ] filter ;
So stretch-word can work like this:
: stretch-word ( string -- stretched )
ascend-string zip ! just zip them in the same order
[
first2 over ! first2 is the only golf I could make :/
[ = ] curry count ! same thing
swap <array> >string ! make an array of char size count and make it a string
] map concat ; ! so you have to join the pieces
Edit:
I think the problem was using repeat to do the job.
: ascend-string ( string -- seqs )
"" [ suffix ] { } accumulate*-as ;
: counts ( string -- counts )
dup ascend-string [ indices length ] { } 2map-as ;
: stretch-word ( string -- stretched )
[ counts ] keep [ <string> ] { } 2map-as concat ;
"bonobo" stretch-word print
bonoobbooo
indices length could also be [ = ] with count

How to refactor code using partial application of quotations?

How can I use existing combinators to refactor this code so that regex will become argument to be partially applied and resulting quotation will have same identical stack effects as ls (x -- )?
USING: io.directories locals sequences accessors math
prettyprint kernel io.files.info io.directories.hierarchy
combinators.short-circuit regexp
;
IN: flac
:: job ( step path -- )
path
[ [ step call ] each ]
with-directory-entries
; inline
:: lsc ( x c -- ) x c call [ x . ] when ; inline
:: ls ( x -- )
x
[ {
[ directory? ]
[ name>> directory-tree-files
[ ".*[.]flac" <regexp> matches? ]
filter length 0 =
]
}
1&&
]
lsc
;
First of all, in the original code, it looks like x is a directory-entry. If x is required to stay as a directory-entry, then it is impossible to refactor out the regex as an argument, after all there's nowhere to put it! If x is allowed to change to, say a string with the regex embedded, or a collection or object, you can then make the regex part of the single argument -- x.
The following solution assumes x can be changed into a tuple object with "dir-entry" and "regex" as slots:
:: ls ( x -- )
x
[ dir-entry>> directory? ].
[
dir-entry>> name>> directory-tree-files
[ [ x regex>> <regexp> matches? ] any? ] [ drop x dir-entry>> ] when .
] smart-when* ;

Evaluating code blocks in Rebol3

I'm trying to improve the Sliding Tile Puzzle example by making the starting positions random.
There's a better way to do this--"It is considered bad practice to convert values to strings and join them together to pass to do for evaluation."--but the approach I took was to try to generate Rebol3 source, and then evaluate it. I have it generating correctly, I think:
random/seed now
arr: random collect [ repeat tilenum 9 [ keep tilenum ] ]
hgroup-data: copy {}
repeat pos 9 [
curtile: (pick arr pos)
append hgroup-data either curtile = 9
[ reduce "x: box tilesize gameback " ]
[ rejoin [ { p "} curtile {" } ] ]
if all [(pos // 3) = 0 pos != 9] [ append hgroup-data " return^/" ]
]
print hgroup-data
...outputs something like:
p "4" x: box tilesize gameback p "5" return
p "3" p "7" p "1" return
p "2" p "8" p "6"
...which if I then copy and paste into this part, works correctly:
view/options [
hgroup [
PASTE-HERE
]
] [bg-color: gameback]
However, if I try to do it dynamically:
view/options [
hgroup [
hgroup-data
]
] [bg-color: gameback]
...(also print hgroup-data, do hgroup-data, and load hgroup-data), I get this error:
** GUI ERROR: Cannot parse the GUI dialect at: hgroup-data
...(or at: print hgroup-data, etc., depending on which variation I tried.)
If I try load [ hgroup-data ] I get:
** Script error: extend-face does not allow none! for its face argument
** Where: either if forever -apply- apply init-layout make-layout actor all foreach do-actor unless -apply- apply all build-face -apply- apply init-layout make-layout actor all foreach do-actor if build-face -apply- apply init-layout make-layout actor all foreach do-actor unless make-face -apply- apply case view do either either either -apply-
** Near: either all [
word? act: dial/1
block? body: get dial...
However, if I use the syntax hgroup do [ hgroup-data ], the program runs, but there are no buttons: it appears to be somehow over-evaluated, so that the return values of the functions p and box and so on are put straight into the hgroup as code.
Surely I'm missing an easy syntax error here. What is it?
First, I would say it's better to construct a block directly, instead of constructing a string and converting it to a block. But if you really want to do that, this should do the trick:
view/options compose/only [
hgroup (load hgroup-data)
] [bg-color: gameback]

Keeping quotations as tuple members in Factor

I want to keep a quotation as a member of a tuple in Factor. But when I try to execute 'call' on it I get the error 'cannot apply call to a run-time computed value'. Note that marking the functions as 'inline' does nothing.
Sample code:
USING: accessors kernel ;
IN: stackoverflow
TUPLE: quottuple quot ;
C: <quottuple> quottuple
: call-quot ( quottuple -- result )
quot>> call ; inline
: main ( -- )
[ 1 ] <quottuple>
call-quot drop ;
MAIN: main
The answer is the 'call(' word. That word requires you to specify the stack effect of the quotation, but as a result the quotation doesn't need to be known at compile time.
USING: accessors kernel ;
IN: stackoverflow
TUPLE: quottuple quot ;
C: <quottuple> quottuple
: call-quot ( quottuple -- result )
quot>> call( -- result ) ;
: main ( -- )
[ 1 ] <quottuple>
call-quot drop ;
MAIN: main

Resources