function that checks for balanced parenthesis in python - recursion

Write a function is_balanced(chars) that takes a string and determines
if the various bracketing characters (e.g. ( ), [ ], and { } ) are
balanced returning True when balanced and False when not. In a
properly formatted Python program these characters should always be in
balance unless they are within string literals. Here are some legal
configurations you might find assuming you ignore all of the
characters that are not bracketing characters (we have included extra
spaces for clarity):
{ ( [ ] ) ( [ ( ) ] ) } ( { { ( ) } } [ ] ) ( { [ ] } ) On the other
hand, these are not legal: ( ( [ ] ) # Missing a close parenthesis
) ( # The close parenthesis comes before the open parenthesis
{ ( } ) # The parentheses and braces are improperly nested Your
solution should be based on the insight that a string consisting only
of bracketing characters is balanced if and only if one of the
following conditions holds:
The string is empty The string contains "( )", "{ }" or "[ ]" as a
substring and is balanced if you remove that substring. To do this
you'll want to use the replace string method.
What I have so far:
def is_balanced(chars):
while len(chars) != 0:
if chars[0] == ")":
return True
elif chars[0] == "(":
is_balanced(chars[1:])
return False
I keep getting a time limit exceeded error.

Related

jq — Extract strings contained in nested arrays and objects

I have this jq filter and input:
( ._birthDate.extension[0] | .url, ( .extension[0] | .url, .valueString ), ( .extension[1] | .url, .valueString ) )
{
"_birthDate":{
"extension":[
{
"url":"http://catsalut.gencat.cat/fhir/StructureDefinition/patient-dataBirhtDeath",
"extension":[
{
"url":"country",
"valueString":"724"
},
{
"url":"state",
"valueString":"08"
}
]
}
]
}
}
…which yields the following output:
"http://catsalut.gencat.cat/fhir/StructureDefinition/patient-dataBirhtDeath"
"country"
"724"
"state"
"08"
I wanted to refactor the filter:
( ._birthDate.extension[0] | .url, ( .extension[:2] | .url, .valueString ) )
…but I am getting the following error:
jq: error (at :18): Cannot index array with string "url"
See this demo.
Array/String Slice: .[10:15] [docs]
... Either index may be negative (in which case it counts backwards from the end of the array), or omitted (in which case it refers to the start or end of the array).
So your were first using .extension[0] that ment: take index 0 from .extension where
.extension[:2] means: take index 0 up and including index 2 from .extension
As #pmf already mentiond, the difference is the returned value, an object at first, but an array on the second.
So you can loop over the array using [] to return the .url and .valueString for each object in side the .extension array:
._birthDate.extension[0] | .url, ( .extension[:2][] | .url, .valueString )
Online Demo
However, since .extension is an array with only 2 indexes, the :2 part doesn't do anything useful in your example, so why not simplify it to:
._birthDate.extension[0] | .url, ( .extension[] | .url, .valueString )
Online Demo
If you only need to keep the strings then you can use .. to recursively traverse your document and strings to filter out non-strings yielded along the way:
.. | strings
Demo

Functx module and long strings with end of chars

I faced a problem with functx module dealing with strings with end of line characters. The following code should work (?)
declare %unit:test function test:substring-before-last() {
let $title := 'Something
blah other'
let $expected-title := 'Something
blah'
return unit:assert-equals(functx:substring-before-last($title, ' other'),
$expected-title)
};
However it gives a failure
"Something
blah" expected, "Something
blah other"
returned.
Removing line breaking makes the test working. What I don't understand? :)
BR
I think the issue is in the definition or implementation of the functx function http://www.xqueryfunctions.com/xq/functx_substring-before-last.html:
declare function functx:substring-before-last
( $arg as xs:string? ,
$delim as xs:string ) as xs:string {
if (matches($arg, functx:escape-for-regex($delim)))
then replace($arg,
concat('^(.*)', functx:escape-for-regex($delim),'.*'),
'$1')
else ''
} ;
and the regular expression dot . matching and the replace default "If the input string contains no substring that matches the regular expression, the result of the function is a single string identical to the input string."; if you add the flags m argument
declare function functx:substring-before-last
( $arg as xs:string? ,
$delim as xs:string ) as xs:string {
if (matches($arg, functx:escape-for-regex($delim)))
then replace($arg,
concat('^(.*)', functx:escape-for-regex($delim),'.*'),
'$1', 'm')
else ''
} ;
you get the right match and replacement and comparison.
Unfortunately, this does not work either reliable. Take this example, where the word 'end is being removed from both lines, not just the last line:

XQuery - wrong indexes in substring after reverse-string function use

Im trying to implement base64 coding in a very simple way. In my approach (lets for a second put away whether its appropriate or not) I need to reverse strings and then concate them. After that this concated string is used in substring function. Strings are joined properly but when I use substring basex seems to lose it.
Funny thing is substring works for well for all indexes starting at 8. So substring($string, 1, 8) and higher gives correct output. But everything below that is messed up. Starting with one disappeared number: substring($string, 1, 7 (and below) ) results in 6 length string.
Moreover substring can start only with 1st or 0 index. Anything greater results in empty return.
declare variable $array := [];
declare function bs:encode
( $input as xs:string ) {
bs:integer-to-binary(string-to-codepoints($input), "", $array)
} ;
declare function bs:integer-to-binary
( $input as xs:integer*, $string as xs:string, $array as array(xs:string) ) {
let $strings :=
for $i in $input
return
if ($i != 0)
then if ($i mod 2 = 0)
then bs:integer-to-binary(xs:integer($i div 2), concat($string, 0), $array)
else bs:integer-to-binary(xs:integer($i div 2), concat($string, 1), $array)
else if ($i <= 0)
then array:append($array, $string)
return bs:check-if-eight($strings)
} ;
declare function bs:check-if-eight
( $strings as item()+ ) {
let $fullBinary :=
for $string in $strings
return if (string-length($string) < 8)
then bs:check-if-eight(concat($string, 0))
else $string (: add as private below :)
return bs:concat-strings($fullBinary)
} ;
declare function bs:concat-strings
( $strings as item()+ ) {
let $firstStringToConcat := functx:reverse-string($strings[position() = 1])
let $secondStringToConcat := functx:reverse-string($strings[position() = 2])
let $thirdStringToConcat := functx:reverse-string($strings[position() = 3])
let $concat :=
concat
($firstStringToConcat,
$secondStringToConcat,
$thirdStringToConcat)
(: this returns correct string of binary value for Cat word :)
return bs:divide-into-six($concat)
} ;
declare function bs:divide-into-six
( $binaryString as xs:string) {
let $sixBitString := substring($binaryString, 1, 6)
(: this should return 010000 instead i get 000100 which is not even in $binaryString at all :)
return $sixBitString
} ;
bs:encode("Cat")
I expect first six letters from string (010000) instead I get some random sequence I guess (00100). The whole module is meant to encode strings into base64 format but for now (the part i uploaded) should just throw first six bits for 'C'
Alright so I figured it out I guess.
First of all in function concat-strings I changed concat to fn:string-join. It allowed me to pass as an argument symbol that separates joined strings.
declare function bs:concat-strings ( $strings as item()+ ) {
let $firstStringToConcat := xs:string(functx:reverse-string($strings[position() = 1]))
let $secondStringToConcat := xs:string(functx:reverse-string($strings[position() = 2]))
let $thirdStringToConcat := xs:string(functx:reverse-string($strings[position() = 3]))
let $concat :=
****fn:string-join(****
($firstStringToConcat,
$secondStringToConcat,
$thirdStringToConcat),****'X'****)
return bs:divide-into-six($concat) } ;
I saw that my input looked like this:
XXXXXXXX01000011XXXXXXXXXXXXXXXXX01100001XXXXXXXXXXXXXXXXX01110100XXXXXXXX
Obviously it had to looping somewhere without clear for loop and as I novice to Xquery i must have been missed that. And indeed. I found it in check-if-eight function:
> declare function bs:check-if-eight ( $strings as item()+ ) {
> **let $fullBinary :=**
> for $string in $strings
> return if (string-length($string) < 8)
> then bs:check-if-eight(concat($string, 0))
> else $string (: add as private below :)
> **return bs:concat-strings($fullBinary)** } ;
Despite being above FOR keyword, $fullBinary variable was in a loop and produced empty spaces(?) and it was clearly shown when i used X as a separator.
DISCLAIMER: I thought about this before and used functx:trim but for some reason it doesnt work like I expected. So it might not for you too if having similar issue.
At this point it was clear that let $fullBinary cannot be bided in FLWR statement at least can't trigger concat-strings function. I changed it and now it produces only string and now im trying to figure out new sequence of running whole module but I think the main problem here is solved.

Parsing an array of dates in XQuery (MarkLogic v8 flavour)

My question seems trivial but could not figure out how to parse a string that contains a list of dates separated by commas. The parsing part of individual dates is not an issue but the empty values are. The trouble is the order of the dates are significant and some dates can be omitted. The dates are expected to be formatted in YYYY-mm-dd
So, the following are valid inputs and expected return values:
,2000-12-12,2012-05-03, ➔ ( NULL, 2000-12-12, 2012-05-03, NULL )
2000-12-12,,2012-05-03 ➔ ( 2000-12-12, NULL, 2012-05-03 )
And here is my function signature
declare function local:assert-date-array-param(
$input as xs:string
, $accept-nulls as xs:boolean?
) as xs:date*
I recognised the problem after realizing that there seems to be no equivalent of of NULL in XQuery for the returned values as placeholders for omitted dates, if you want to return a sequence, that is. Because empty sequences wrapped inside sequences are flattened to nothing.
I suppose, my fallback would be to use date like 1900-01-01 as the placeholder or return a map instead of a sequence, but I sure hope to find a more elegant way
Thank you,
K.
PS. I am working with MarkLogic v8 (and v9 soon) and any solution should execute with their XQuery processor.
UPDATE: thanks for both answers, in the end I chose to go with a placeholder date as XQuery works so nicely with sequences and anything else would have required some changes in other places. But the problem remains for the cases where the required return values are numerics. In this case, use of placeholder values would probably be not feasible. A null literal for xs:anyAtomicType would have solved the problem nicely, but alas.
You could consider returning a json:array(), or an array-node{} with null-node{}'s inside. But perhaps a null-date placeholder is not as bad as it sounds:
declare variable $null-date := xs:date("0001-01-01");
declare function local:assert-date-array-param(
$input as xs:string,
$accept-nulls as xs:boolean?
) as xs:date*
{
for $d in fn:tokenize($input, "\s*,\s*")
return
if ($d eq "") then
if ($accept-nulls) then
$null-date
else
fn:error(xs:QName("NULL-NOT-ALLOWED"), "Date is required")
else
if ($d castable as xs:date) then
xs:date($d)
else if ($d castable as xs:dateTime) then
xs:date(xs:dateTime($d))
else
fn:error(xs:QName("INVALID-DATE"), "Invalid date format: " || $d)
};
declare function local:print-date-array($dates) {
string-join(for $d in $dates return if ($d eq $null-date) then "NULL" else fn:string($d), ", ")
};
local:print-date-array(
local:assert-date-array-param(",2000-12-12,2012-05-03,", fn:true())
),
local:print-date-array(
local:assert-date-array-param("2000-12-12,,2012-05-03", fn:true())
)
HTH!
Multiple options .. in addition to above.
return a sequence of functions which when invoked return dates
for $i in string-tokenize-to-sequence-of-strings()
let $dt := my-parse-date($i)
return function() { $dt ;}
or
return function() { return my-parse-date($i) ;
return tokenized and validated but not parsed strings. Use "" for 'not valid', e.g.:
( "2014-01-22","","2017-03-30","" )
then there's arrays', maps, arrays of maps, and ... XML
parseFunction() as xs:element()*:
for ... return <date>{ parse-and-validate($value) } </date>

Complex-recursive stack effects?

USING: accessors html.parser.analyzer io kernel math namespaces
present regexp sequences ;
IN: all-roads-to-wiki
SYMBOL: G
: match-good-pages ( a -- ?/f )
R/ \/wiki\/[^:]*$/ first-match ;
: filter-urls ( tags -- urls )
find-hrefs [ present ] map
[ match-good-pages ] filter
[ match-good-pages seq>> ] map ;
: findpath ( url -- url )
G get =
[
! false
]
[ scrape-html nip
[
dup "title" find-by-name drop 1 + swap nth
text>> R/ - Wikipedia,/ re-split first print
]
[
"bodyContent" find-by-id-between filter-urls [ findpath ] map
] bi
] if ; inline recursive
: allroads-entry ( -- a )
readln "http://en.wikipedia.org/wiki/" prepend G set-global
"enwp.org/Special:Random" findpath ; inline
The above code will recurse over every link on Wikipedia until it finds the one it's looking for.
That's okay, because (hopefully) findpath will eventually "return" (i.e. not call itself again) and leave a huge nested data structure on the stack. But when I try to compile this, I get an unbalanced-recursion error:
The recursive word “findpath” leaves with the stack having the wrong height
unbalanced-recursion: Thrown when stack effect inference determines that an inline recursive word has an incorrect stack effect declaration.
No matter what I do, Factor (understandably) complains about the stack effect not matching up. What do I have to do to get this to recurse properly?
Look closely at the find-path word. I'll add comments so that you can see what is on the stack:
: findpath ( url -- url )
! 1 item: { url }
G
! 2 items: { url G }
get
! 2 items: { url value-of-G }
=
! 1: item { t/f }
[
! 0 items!!!!
! false
]
[ scrape-html nip
[
dup "title" find-by-name drop 1 + swap nth
text>> R/ - Wikipedia,/ re-split first print
]
[
"bodyContent" find-by-id-between filter-urls
[ findpath ] map
] bi
] if ; inline recursive
The if combinator consumes the last item on the stack so this code can't possibly work. Here is working code for the findpath word:
: page-title ( seq -- title )
dup "title" find-by-name drop 1 + swap nth
text>> R/ - Wikipedia,/ re-split first ;
: page-links ( seq -- links )
"bodyContent" find-by-id-between filter-urls ;
: scrape-en-wiki-url ( wiki-url -- seq )
"https://en.wikipedia.org" prepend
dup print flush scrape-html nip ;
: found-url? ( wiki-url -- ? )
G get [ = ] [ drop t ] if* ;
: findpath ( wiki-url -- seq/f )
dup found-url?
[ drop f G set f ] [
scrape-en-wiki-url
[ page-title print flush ] [
page-links [ findpath ] map
] bi
] if ; inline recursive
Also take a look at the Wikipedia vocab which is meant for tasks like these.

Resources