Groovyで文字列の一部を破壊的に削除する(Groovyならできるらしいのだが・・・)
Groovyで文字列の途中に破壊的に文字列を挿入する
で以下のコメントをもらった
delegateがよく分からないので、試さずそのままコメントを載せてた。
Groovy イン・アクションを参考にdelegateを使って、
Groovyで文字列の一部を破壊的に削除する
をやり直そうと思って試した。
しかし、コメントで教えてもらったコードが動かなかった。。。
コード
String.metaClass.insert << { i, s -> def chars = delegate.value as List; chars.addAll(i, s as List); delegate.value = chars as char[]; delegate.count = chars.size() } def s = 'groovy' s.insert(3, 'groovy') s
実行結果
Caught: groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: value for class: java.lang.String at Q061_文字列の一部を破壊的に削除する_delegate版$_run_closure1.doCall(Q061_文字列の一部を破壊的に削除する_delegate版.groovy:4) at Q061_文字列の一部を破壊的に削除する_delegate版.run(Q061_文字列の一部を破壊的に削除する_delegate版.groovy:9)
1.5.8、1.6.0、1.6.5, 1.7.8, 1.8.0で試してみたけどダメだった。
これは動いた。
『「プログラミングGroovy」のカバーに載せる短い(3行程度)プログラム。簡潔だがGroovyのパワーや特徴が良くでたやつ』
String.metaClass.hello = { "Hello, $delegate!" } println "Groovy".hello()
Hello, Groovy!
Groovy イン・アクション P42,122,182を読んでみたが
なぜ、だめなのか、どうすればいいのか分からない。
疑問1
1行目は String.metaClass.insert << { であってるの?
String.metaClass.insert = { ではないの?
=でもだめだったけど。
正しいとするとするとこの << ってどういう意味?
疑問2
2行目のdef chars = delegate.value as List;
4行目のdelegate.value = chars as char;
Listとcharで方が違うけどこれが原因かとも思ったけど、
コメントアウトしても、次の行の delegate.count = chars.size() で例外に
なってしまう。
delegateって使うのが正しいの??
教えてスーパーなGroovyの人〜
'groovy'から"groovy"に変更
String.metaClass.insert << { i, s -> def chars = delegate.value as List; chars.addAll(i, s as List); delegate.value = chars as char[]; delegate.count = chars.size() } def s = "groovy" s.insert(3, "groovy") s
GroovyConsoleでの結果
変わらず・・・
Exception thrown 2011/06/22 21:58:29 org.codehaus.groovy.runtime.StackTraceUtils sanitize 警告: Sanitizing stacktrace: groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: value for class: java.lang.String at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2353) at groovy.lang.ExpandoMetaClass.setProperty(ExpandoMetaClass.java:1128) at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:3312) at org.codehaus.groovy.runtime.InvokerHelper.setProperty(InvokerHelper.java:183) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.setProperty(ScriptBytecodeAdapter.java:480) at ConsoleScript5$_run_closure1.doCall(ConsoleScript5:4) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) at org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:80) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120) at ConsoleScript5.run(ConsoleScript5:9) at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:266) at groovy.lang.GroovyShell.run(GroovyShell.java:517) at groovy.lang.GroovyShell.run(GroovyShell.java:172) at groovy.lang.GroovyShell$run.call(Unknown Source) at groovy.ui.Console$_runScriptImpl_closure16.doCall(Console.groovy:910) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:885) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141) at groovy.ui.Console$_runScriptImpl_closure16.doCall(Console.groovy) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:885) at groovy.lang.Closure.call(Closure.java:405) at groovy.lang.Closure.call(Closure.java:399) at groovy.lang.Closure.run(Closure.java:483) at java.lang.Thread.run(Thread.java:662) groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: value for class: java.lang.String at ConsoleScript5$_run_closure1.doCall(ConsoleScript5:4) at ConsoleScript5.run(ConsoleScript5:9)
String.metaClass.insert << { i, s -> def chars = delegate.value as List; chars.addAll(i, s as List); delegate.@value = chars as char[]; delegate.@count = chars.size() } def s = 'groovy' s.insert(3, 'groovy') s
GroovyConsoleの結果
groovy> String.metaClass.insert << { i, s -> groovy> def chars = delegate.value as List; groovy> chars.addAll(i, s as List); groovy> delegate.@value = chars as char[]; groovy> delegate.@count = chars.size() groovy> } groovy> def s = 'groovy' groovy> s.insert(3, 'groovy') groovy> s Exception thrown 2011/06/22 22:48:07 org.codehaus.groovy.runtime.StackTraceUtils sanitize 警告: Sanitizing stacktrace: groovy.lang.GroovyRuntimeException: Cannot set the property 'value' because the backing field is final. at org.codehaus.groovy.reflection.CachedField.setProperty(CachedField.java:68) at groovy.lang.MetaClassImpl.setAttribute(MetaClassImpl.java:2518) at groovy.lang.MetaClassImpl.setAttribute(MetaClassImpl.java:3320) at org.codehaus.groovy.runtime.InvokerHelper.setAttribute(InvokerHelper.java:145) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.setField(ScriptBytecodeAdapter.java:328) at ConsoleScript0$_run_closure1.doCall(ConsoleScript0:4) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) at org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:80) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120) at ConsoleScript0.run(ConsoleScript0:9) at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:266) at groovy.lang.GroovyShell.run(GroovyShell.java:517) at groovy.lang.GroovyShell.run(GroovyShell.java:172) at groovy.lang.GroovyShell$run.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124) at groovy.ui.Console$_runScriptImpl_closure16.doCall(Console.groovy:910) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:885) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141) at groovy.ui.Console$_runScriptImpl_closure16.doCall(Console.groovy) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:885) at groovy.lang.Closure.call(Closure.java:405) at groovy.lang.Closure.call(Closure.java:399) at groovy.lang.Closure.run(Closure.java:483) at java.lang.Thread.run(Thread.java:662) groovy.lang.GroovyRuntimeException: Cannot set the property 'value' because the backing field is final. at ConsoleScript0$_run_closure1.doCall(ConsoleScript0:4) at ConsoleScript0.run(ConsoleScript0:9)
確かに'value' が final だからだめのようだ。
1.5.6かよ・・・
ダンロード
http://dist.groovy.codehaus.org/distributions/groovy-binary-1.5.6.zip
GroovyConsleの結果
groovy> String.metaClass.insert << { i, s -> groovy> def chars = delegate.value as List; groovy> chars.addAll(i, s as List); groovy> delegate.value = chars as char[]; groovy> delegate.count = chars.size() groovy> } groovy> def s = 'groovy' groovy> s.insert(3, 'groovy') groovy> s Result: "grogroovyovy"
ホントだ。Groovy 1.5.6だと動くや。
コメントより
1.5.6では動きますが、現在のバージョンではfinal変数への再代入は
許可されなくなったようです。なんか悩ませてしまったようでごめんなさい。
なんか悔しいからfinal許可するように書き換えて
やろうかと思いましたが大人気ないので止めました。では。
結論
Groovy 1.5.7以降は、value、countは final だから更新できない。
えっ違うの?
さらにツッコミが来た。
String.metaClass.destructiveReverse << { def chars = delegate.value; chars.eachWithIndex {e, i -> chars[- i - 1] = e } } def s = 'groovy' s.destructiveReverse() assert s == 'yvoorg'
GroovyConsoleの結果(Groovy 1.8.0)
groovy> String.metaClass.destructiveReverse << { groovy> def chars = delegate.value; groovy> chars.eachWithIndex {e, i -> groovy> chars[- i - 1] = e groovy> } groovy> } groovy> def s = 'groovy' groovy> s.destructiveReverse() groovy> s Result: "yvoorg"
わぉ
詳細は『プログラミングGroovy』で!!
・・・・記載があるのだろうか??
スーパーなGroovyな方々に、いろいろアドバイスをいただいて助かりました。
本当にありがとうございました。m(__)m
実行環境
- OS:Windows XP
- Groovy Version:1.5.6、1.5.8、1.6.0、1.6.5、1.7.8、1.8.0
- JVM: 1.6.0_26
Groovyの詳細についてはJavadocと以下の書籍を参考にしている。
Dierk Konig、Andrew Glover、Paul King、、Guillaume Laforge、Jon Skeet、杉浦 孝、櫻井 正樹、須江 信洋、関谷 和愛、佐野 徹郎、寺沢 尚史
Amazonでご確認ください。
問題自体は第2版のもの。rubyと似てる部分も多いので、ヒントにもなる。
写経でもいいが自分で考えるために他言語の例をGroovyで置き換えてる。
Groovyイン・アクションを読むならあった方が便利かな。